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.widgets;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsConfigurationReader;
032import org.opencms.ade.configuration.formatters.CmsFormatterChangeSet;
033import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCacheState;
034import org.opencms.file.CmsFile;
035import org.opencms.file.CmsObject;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.i18n.CmsMessages;
039import org.opencms.main.CmsException;
040import org.opencms.main.CmsLog;
041import org.opencms.main.OpenCms;
042import org.opencms.util.CmsUUID;
043import org.opencms.workplace.CmsWorkplaceMessages;
044import org.opencms.xml.containerpage.I_CmsFormatterBean;
045import org.opencms.xml.content.CmsXmlContent;
046import org.opencms.xml.content.CmsXmlContentFactory;
047import org.opencms.xml.types.A_CmsXmlContentValue;
048
049import java.util.Comparator;
050import java.util.HashSet;
051import java.util.List;
052import java.util.Locale;
053import java.util.Set;
054import java.util.SortedSet;
055import java.util.TreeSet;
056
057import org.apache.commons.logging.Log;
058
059import com.google.common.collect.ComparisonChain;
060import com.google.common.collect.Lists;
061
062/**
063 * Abstract superclass for widgets used to enable or disable formatters.<p>
064 */
065public abstract class A_CmsFormatterWidget extends CmsSelectWidget {
066
067    /**
068     * Comparator used to sort formatter beans in the order in which they should be displayed in the selection.<p>
069     */
070    public static class FormatterSelectComparator implements Comparator<I_CmsFormatterBean> {
071
072        /**
073         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
074         */
075        public int compare(I_CmsFormatterBean first, I_CmsFormatterBean second) {
076
077            SortedSet<String> firstSet = new TreeSet<String>(first.getResourceTypeNames());
078            SortedSet<String> secondSet = new TreeSet<String>(second.getResourceTypeNames());
079            return ComparisonChain.start().compare(firstSet.toString(), secondSet.toString()).compare(
080                first.getRank(),
081                second.getRank()).result();
082        }
083    }
084
085    /** The logger instance for this class. */
086    protected static final Log LOG = CmsLog.getLog(A_CmsFormatterWidget.class);
087
088    /**
089     * Creates a widget option corresponding to a formatter bean for an external formatter.<p>
090     *
091     * @param cms the current CMS context
092     * @param formatter the formatter bean
093     *
094     * @return the select option which was created
095     *
096     * @throws Exception in case reading the formatter configuration file fails
097     */
098    public static CmsSelectWidgetOption getWidgetOptionForFormatter(CmsObject cms, I_CmsFormatterBean formatter)
099    throws Exception {
100
101        String name = formatter.getNiceName(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms))
102            + " "
103            + formatter.getResourceTypeNames().toString()
104            + "  "
105            + " ("
106            + formatter.getJspRootPath()
107            + ")";
108        String path = cms.readResource(
109            new CmsUUID(formatter.getId()),
110            CmsResourceFilter.ONLY_VISIBLE_NO_DELETED).getRootPath();
111        CmsSelectWidgetOption option = new CmsSelectWidgetOption(path, false, name);
112        return option;
113    }
114
115    /**
116     * Creates a widget option for a resource type.<p>
117     *
118     * @param cms the current CMS context
119     * @param typeName the type for which we want a widget option
120     *
121     * @return the created widget option
122     */
123    public static CmsSelectWidgetOption getWidgetOptionForType(CmsObject cms, String typeName) {
124
125        String niceTypeName = typeName;
126        try {
127            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
128            niceTypeName = CmsWorkplaceMessages.getResourceTypeName(locale, typeName);
129        } catch (@SuppressWarnings("unused") Exception e) {
130            // resource type name will be used as a fallback
131        }
132        CmsSelectWidgetOption option = new CmsSelectWidgetOption(
133            CmsFormatterChangeSet.keyForType(typeName),
134            false,
135            getMessage(cms, Messages.GUI_SCHEMA_FORMATTER_OPTION_1, niceTypeName));
136        return option;
137    }
138
139    /**
140     * Gets a message string.<p>
141     *
142     * @param cms the CMS context
143     * @param message the message key
144     * @param args the message arguments
145     *
146     * @return the message string
147     */
148    static String getMessage(CmsObject cms, String message, Object... args) {
149
150        Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
151        return Messages.get().getBundle(locale).key(message, args);
152    }
153
154    /**
155     * @see org.opencms.widgets.I_CmsADEWidget#getConfiguration(org.opencms.file.CmsObject, org.opencms.xml.types.A_CmsXmlContentValue, org.opencms.i18n.CmsMessages, org.opencms.file.CmsResource, java.util.Locale)
156     */
157    @Override
158    public String getConfiguration(
159        CmsObject cms,
160        A_CmsXmlContentValue schemaType,
161        CmsMessages messages,
162        CmsResource resource,
163        Locale contentLocale) {
164
165        CmsDummyWidgetDialog widgetDialog = new CmsDummyWidgetDialog(messages.getLocale(), messages);
166        widgetDialog.setResource(resource);
167        String result = getConfiguration();
168        result += "||";
169        result += CmsSelectWidgetOption.createConfigurationString(
170            parseSelectOptions(cms, widgetDialog, schemaType, false));
171        result += "||";
172        result += CmsSelectWidgetOption.createConfigurationString(
173            parseSelectOptions(cms, widgetDialog, schemaType, true));
174        return result;
175    }
176
177    /**
178     * @see org.opencms.widgets.I_CmsADEWidget#getWidgetName()
179     */
180    @Override
181    public String getWidgetName() {
182
183        return A_CmsFormatterWidget.class.getName();
184    }
185
186    /**
187     * Gets the options corresponding to external (non-schema) formatters.<p>
188     *
189     * @param cms the CMS context
190     * @param config the ADE configuration
191     * @param rootPath the root path of the edited file
192     * @param allRemoved flag, indicating if all inheritedly available formatters should be disabled
193     *
194     * @return the select widget options for the external formatters
195     */
196    protected abstract List<CmsSelectWidgetOption> getFormatterOptions(
197        CmsObject cms,
198        CmsADEConfigData config,
199        String rootPath,
200        boolean allRemoved);
201
202    /**
203     * Gets the values which have already been selected in the edited resource on the VFS.<p>
204     *
205     * @param reader a sitemap configuration reader
206     * @param content the unmarshalled content
207     *
208     * @return the set of values which have already been selected
209     */
210    protected abstract Set<String> getSelectedInFile(CmsConfigurationReader reader, CmsXmlContent content);
211
212    /**
213     * Gets the options corresponding to the schemas which define formatters.<p>
214     *
215     * @param cms the current CMS context
216     * @param config the ADE configuration
217     * @param allRemoved flag, indicating if all inheritedly available formatters should be disabled
218     *
219     * @return the select widget options for the content types with formatters in the schema
220     */
221    protected abstract List<CmsSelectWidgetOption> getTypeOptions(
222        CmsObject cms,
223        CmsADEConfigData config,
224        boolean allRemoved);
225
226    /**
227     * @see org.opencms.widgets.A_CmsSelectWidget#parseSelectOptions(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter)
228     */
229    @Override
230    protected List<CmsSelectWidgetOption> parseSelectOptions(
231        CmsObject cms,
232        I_CmsWidgetDialog widgetDialog,
233        I_CmsWidgetParameter param) {
234
235        List<CmsSelectWidgetOption> options = Lists.newArrayList();
236        options.add(new CmsSelectWidgetOption("", true, getMessage(cms, Messages.GUI_FORMATTER_EMPTY_SELECTION_0)));
237        return options;
238    }
239
240    /**
241     * Returns the list of configured select options, parsing the configuration String if required.<p>
242     *
243     * The list elements are of type <code>{@link CmsSelectWidgetOption}</code>.
244     * The configuration String is parsed only once and then stored internally.<p>
245     *
246     * @param cms the current users OpenCms context
247     * @param widgetDialog the dialog of this widget
248     * @param param the widget parameter of this dialog
249     * @param allRemoved flag, indicating if all inheritedly available formatters should be disabled
250     *
251     * @return the list of select options
252     *
253     * @see CmsSelectWidgetOption
254     */
255    protected List<CmsSelectWidgetOption> parseSelectOptions(
256        CmsObject cms,
257        I_CmsWidgetDialog widgetDialog,
258        I_CmsWidgetParameter param,
259        boolean allRemoved) {
260
261        String path = getResourcePath(cms, widgetDialog);
262        try {
263            cms = OpenCms.initCmsObject(cms);
264            cms.getRequestContext().setSiteRoot("");
265            CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(cms, path);
266            if (adeConfig.parent() != null) {
267                adeConfig = adeConfig.parent();
268            }
269            Set<String> added = new HashSet<String>();
270            List<CmsSelectWidgetOption> options = Lists.newArrayList();
271            options.add(new CmsSelectWidgetOption("", true, getMessage(cms, Messages.GUI_FORMATTER_EMPTY_SELECTION_0)));
272            List<CmsSelectWidgetOption> formatterOptions = getFormatterOptions(cms, adeConfig, path, allRemoved);
273            options.addAll(formatterOptions);
274            List<CmsSelectWidgetOption> typeOptions = getTypeOptions(cms, adeConfig, allRemoved);
275            options.addAll(typeOptions);
276            for (CmsSelectWidgetOption option : options) {
277                added.add(option.getValue());
278            }
279            try {
280                CmsResource content = cms.readResource(path);
281                CmsFile contentFile = cms.readFile(content);
282                CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(cms, contentFile);
283                CmsConfigurationReader reader = new CmsConfigurationReader(cms);
284                Set<String> selected = getSelectedInFile(reader, xmlContent);
285                for (String formatterKey : selected) {
286                    if (CmsUUID.isValidUUID(formatterKey)) {
287                        CmsFormatterConfigurationCacheState cacheState = OpenCms.getADEManager().getCachedFormatters(
288                            cms.getRequestContext().getCurrentProject().isOnlineProject());
289                        CmsUUID mapKey = new CmsUUID(formatterKey);
290                        I_CmsFormatterBean formatter = cacheState.getFormatters().get(mapKey);
291                        if (formatter != null) {
292                            try {
293                                CmsSelectWidgetOption option = A_CmsFormatterWidget.getWidgetOptionForFormatter(
294                                    cms,
295                                    formatter);
296
297                                if (!added.contains(option.getValue())) {
298                                    options.add(option);
299                                }
300                            } catch (Exception e) {
301                                LOG.error(e.getLocalizedMessage(), e);
302                            }
303                        }
304                    }
305
306                }
307
308            } catch (CmsException e) {
309                LOG.error(e.getLocalizedMessage(), e);
310            }
311            return options;
312        } catch (CmsException e) {
313            // should never happen
314            LOG.error(e.getLocalizedMessage(), e);
315            return null;
316        }
317    }
318
319}