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.ade.galleries;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.CmsVfsResourceNotFoundException;
034import org.opencms.main.CmsLog;
035import org.opencms.main.OpenCms;
036import org.opencms.search.galleries.CmsGallerySearch;
037import org.opencms.search.galleries.CmsGallerySearchResult;
038import org.opencms.security.CmsSecurityException;
039import org.opencms.util.CmsMacroResolver;
040import org.opencms.util.CmsUUID;
041import org.opencms.xml.content.I_CmsXmlContentLocation;
042import org.opencms.xml.content.I_CmsXmlContentValueLocation;
043import org.opencms.xml.types.CmsXmlVfsFileValue;
044
045import java.util.ArrayList;
046import java.util.Collection;
047import java.util.Collections;
048import java.util.HashMap;
049import java.util.List;
050import java.util.Locale;
051import java.util.Map;
052import java.util.Set;
053
054import org.apache.commons.logging.Log;
055
056/**
057 * Replacement configuration for the 'add content' dialog.
058 *
059 * <p>A replacement configuration contains a list of entries, one for each type,
060 * with a list of resources for each type and optionally a title string for each of the resources.
061 * The list of resources replaces the search results that would be normally shown for the type
062 * in the 'add content' dialog.
063 */
064public class CmsAddContentRestriction {
065
066    /**
067     * Contains the replacements (and titles of the replacements) for a single type.
068     */
069    public static class TypeEntry {
070
071        /** The location from which this entry was read. */
072        private String m_origin;
073
074        /** The replacement titles for the search results, with the corresponding structure ids as keys. */
075        private Map<CmsUUID, String> m_replacedTitles = new HashMap<>();
076
077        /** The resources to replace the search result for the configured type with. */
078        private List<CmsResource> m_resources = new ArrayList<>();
079
080        /** The name of the resource type. */
081        private String m_type;
082
083        /**
084         * Creates a new entry.
085         *
086         * @param type the name of the resource type
087         * @param resources the resources to use as a replacement
088         * @param titleReplacements a map from structure ids to replacement titles for those
089         * @param origin the place from which this entry was read
090         */
091        public TypeEntry(
092            String type,
093            List<CmsResource> resources,
094            Map<CmsUUID, String> titleReplacements,
095            String origin) {
096
097            m_type = type;
098            m_resources = new ArrayList<>(resources);
099            m_replacedTitles = new HashMap<>(titleReplacements);
100            m_origin = origin;
101        }
102
103        /**
104         * Gets the location from which this entry was read.
105         *
106         * @return the location from which the entry was read
107         */
108        public String getOrigin() {
109
110            return m_origin;
111        }
112
113        /**
114         * Gets the results to be displayed in the 'add content' dialog.
115         *
116         * @param cms the CMS context
117         * @return the results to display
118         */
119        @SuppressWarnings("synthetic-access")
120        public List<CmsGallerySearchResult> getResults(CmsObject cms) {
121
122            List<CmsGallerySearchResult> result = new ArrayList<>();
123            Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
124            CmsMacroResolver macroResolver = new CmsMacroResolver();
125            macroResolver.setCmsObject(cms);
126            macroResolver.setMessages(OpenCms.getWorkplaceManager().getMessages(locale));
127
128            for (CmsResource res : m_resources) {
129                try {
130                    // Read resources again because the user may not be allowed to access them,
131                    // or they may have been moved since the replacement configuration was read
132                    CmsResource currentRes = cms.readResource(
133                        res.getStructureId(),
134                        CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
135
136                    CmsGallerySearchResult singleResult = CmsGallerySearch.searchById(
137                        cms,
138                        res.getStructureId(),
139                        locale);
140                    String replacementTitle = m_replacedTitles.get(currentRes.getStructureId());
141                    if (replacementTitle != null) {
142                        replacementTitle = macroResolver.resolveMacros(replacementTitle);
143                        singleResult = singleResult.withTitle(replacementTitle);
144                    }
145                    result.add(singleResult);
146                } catch (CmsVfsResourceNotFoundException | CmsSecurityException e) {
147                    LOG.debug("filtered resource " + res.getRootPath() + " (" + res.getStructureId() + ")");
148                } catch (Exception e) {
149                    LOG.error(e.getLocalizedMessage(), e);
150                }
151            }
152            return result;
153        }
154
155        /**
156         * Gets the type.
157         *
158         * @return the type
159         */
160        public String getType() {
161
162            return m_type;
163        }
164    }
165
166    /** Empty configuration. */
167    public static final CmsAddContentRestriction EMPTY = new CmsAddContentRestriction();
168
169    /** XML node name. */
170    public static final String N_ENTRY = "Entry";
171
172    /** XML node name. */
173    public static final String N_RESOURCE = "Resource";
174
175    /** XML node name. */
176    public static final String N_TITLE = "Title";
177
178    /** XML node name. */
179    public static final String N_TYPE = "Type";
180
181    /** The name of the resource type from which the configuration is read. */
182    public static final String TYPE_NAME = "add_content_replacement";
183
184    /** Logger instance for this class. */
185    private static final Log LOG = CmsLog.getLog(CmsAddContentRestriction.class);
186
187    /** The configuration entries, with their types as keys. */
188    private Map<String, TypeEntry> m_entries = new HashMap<>();
189
190    /**
191     * Creates a new configuration from a list of entries.
192     *
193     * @param entries the list of configuration entries
194     */
195    public CmsAddContentRestriction(Collection<TypeEntry> entries) {
196
197        for (TypeEntry replacer : entries) {
198            m_entries.put(replacer.getType(), replacer);
199        }
200    }
201
202    /**
203     * Creates an empty configuration where nothing is replaced.
204     */
205    private CmsAddContentRestriction() {
206
207        // do nothing
208    }
209
210    /**
211     * Reads a content restriction from an XML content.
212     *
213     * @param cms the CMS context
214     * @param parent the parent location
215     * @param nodeName the name of the XML elements containing the content restrictions
216     *
217     * @return the content restriction
218     */
219    public static CmsAddContentRestriction read(CmsObject cms, I_CmsXmlContentLocation parent, String nodeName) {
220
221        List<TypeEntry> entries = new ArrayList<>();
222        for (I_CmsXmlContentValueLocation entryLoc : parent.getSubValues(nodeName)) {
223            TypeEntry entry = readEntry(cms, entryLoc);
224            entries.add(entry);
225        }
226        return new CmsAddContentRestriction(entries);
227
228    }
229
230    /**
231     * Reads the entry for a single type from the configuration.
232     *
233     * @param cms the CMS context
234     * @param location the location from which to read the entry
235     *
236     * @return the  entry that was read
237     */
238    public static TypeEntry readEntry(CmsObject cms, I_CmsXmlContentValueLocation location) {
239
240        String type = location.getSubValue(N_TYPE).getValue().getStringValue(cms).trim();
241        Map<CmsUUID, String> titleMap = new HashMap<>();
242        List<CmsResource> resourceList = new ArrayList<>();
243        for (I_CmsXmlContentValueLocation entryLoc : location.getSubValues(N_ENTRY)) {
244            CmsXmlVfsFileValue resoureValue = (CmsXmlVfsFileValue)(entryLoc.getSubValue(N_RESOURCE).getValue());
245            CmsResource resource = resoureValue.getLink(cms).getResource();
246            if (resource != null) {
247                resourceList.add(resource);
248                I_CmsXmlContentValueLocation titleLoc = entryLoc.getSubValue(N_TITLE);
249                if (titleLoc != null) {
250                    String title = titleLoc.getValue().getStringValue(cms);
251                    titleMap.put(resource.getStructureId(), title);
252                }
253            }
254        }
255        return new TypeEntry(type, resourceList, titleMap, location.getValue().getDocument().getFile().getRootPath());
256    }
257
258    /**
259     * Gets the replaced results for a specific resource type.
260     *
261     * @param cms the CMS context
262     * @param type the type name
263     *
264     * @return the replacement results for the given type
265     */
266    public List<CmsGallerySearchResult> getResult(CmsObject cms, String type) {
267
268        TypeEntry replacer = m_entries.get(type);
269        if (replacer == null) {
270            return null;
271        }
272        return replacer.getResults(cms);
273    }
274
275    /**
276     * Gets the set of names of types for which the search results are replaced.
277     *
278     * @return the set of types for which there are replacements configured
279     */
280    public Set<String> getTypes() {
281
282        return Collections.unmodifiableSet(m_entries.keySet());
283    }
284
285    /**
286     * Merges this configuration and a child configuration into a new configuration object, where an entry for a type in the child
287     * overrides an entry for the same type in the parent.
288     *
289     * @param child the child configuration
290     * @return the merged configuration
291     */
292    public CmsAddContentRestriction merge(CmsAddContentRestriction child) {
293
294        Map<String, TypeEntry> combinedMap = new HashMap<>();
295        combinedMap.putAll(m_entries);
296        combinedMap.putAll(child.m_entries);
297        return new CmsAddContentRestriction(combinedMap.values());
298
299    }
300
301}