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.containerpage;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsElementView;
032import org.opencms.ade.configuration.CmsResourceTypeConfig;
033import org.opencms.ade.configuration.CmsResourceTypeConfig.AddMenuType;
034import org.opencms.ade.configuration.CmsResourceTypeConfig.AddMenuVisibility;
035import org.opencms.ade.galleries.CmsGalleryService;
036import org.opencms.ade.galleries.shared.CmsResourceTypeBean;
037import org.opencms.ade.galleries.shared.CmsResourceTypeBean.Origin;
038import org.opencms.file.CmsObject;
039import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
040import org.opencms.file.types.I_CmsResourceType;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.security.CmsPermissionSet;
045import org.opencms.security.CmsRole;
046import org.opencms.util.CmsUUID;
047import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
048
049import java.util.ArrayList;
050import java.util.Collections;
051import java.util.Comparator;
052import java.util.HashSet;
053import java.util.List;
054import java.util.Map;
055import java.util.Set;
056
057import org.apache.commons.logging.Log;
058
059import com.google.common.collect.ArrayListMultimap;
060import com.google.common.collect.ComparisonChain;
061import com.google.common.collect.Lists;
062import com.google.common.collect.Maps;
063import com.google.common.collect.Multimap;
064import com.google.common.collect.Sets;
065
066/**
067 * Helper class for preparing the resource type lists for gallery and new dialog.<p>
068 *
069*/
070public class CmsAddDialogTypeHelper {
071
072    /** Logger instance for this class. */
073    private static final Log LOG = CmsLog.getLog(CmsAddDialogTypeHelper.class);
074
075    /** All types from the ADE config previously processed. */
076    private Set<String> m_allAdeTypes = Sets.newHashSet();
077
078    /** Cached type lists. */
079    private Multimap<CmsUUID, CmsResourceTypeBean> m_cachedTypes;
080
081    /** All types from the ADE config previously included in a result list. */
082    private Set<String> m_includedAdeTypes = Sets.newHashSet();
083
084    /** The menu type. */
085    private AddMenuType m_menuType;
086
087    /**
088     * Creates a new instance.<p>
089     *
090     * @param type the menu type for which we want to build a type list
091     */
092    public CmsAddDialogTypeHelper(AddMenuType type) {
093
094        LOG.debug("Creating type helper.");
095        m_menuType = type;
096    }
097
098    /**
099     * Gets the precomputed type list for the given view.<p>
100     *
101     * @param view the element view
102     * @return the precomputed type list, or null if the list wasn't precomputed
103     */
104    public List<CmsResourceTypeBean> getPrecomputedTypes(CmsElementView view) {
105
106        return Lists.newArrayList(m_cachedTypes.get(view.getId()));
107    }
108
109    /**
110     * Creates list of resource type beans for gallery or 'New' dialog.<p>
111     *
112     * @param cms the CMS context
113     * @param folderRootPath the current folder
114     * @param checkViewableReferenceUri the reference uri to use for viewability check
115     * @param elementView the element view
116     * @param checkEnabled object to check whether resource types should be enabled
117     *
118     * @return the list of resource type beans
119     *
120     * @throws CmsException if something goes wrong
121     */
122    public List<CmsResourceTypeBean> getResourceTypes(
123        CmsObject cms,
124        String folderRootPath,
125        String checkViewableReferenceUri,
126        CmsElementView elementView,
127        I_CmsResourceTypeEnabledCheck checkEnabled)
128    throws CmsException {
129
130        if (elementView == null) {
131            LOG.error("Element view is null");
132            return Collections.emptyList();
133        }
134        List<I_CmsResourceType> additionalTypes = Lists.newArrayList();
135
136        // First store the types in a map, to avoid duplicates
137        Map<String, I_CmsResourceType> additionalTypeMap = Maps.newHashMap();
138
139        if (elementView.getExplorerType() != null) {
140            List<CmsExplorerTypeSettings> explorerTypes = OpenCms.getWorkplaceManager().getExplorerTypesForView(
141                elementView.getExplorerType().getName());
142            for (CmsExplorerTypeSettings explorerType : explorerTypes) {
143                if (elementView.isOther() && m_includedAdeTypes.contains(explorerType.getName())) {
144                    continue;
145                }
146                additionalTypeMap.put(
147                    explorerType.getName(),
148                    OpenCms.getResourceManager().getResourceType(explorerType.getName()));
149            }
150        }
151        if (OpenCms.getRoleManager().hasRole(cms, CmsRole.DEVELOPER) && elementView.isOther()) {
152            Set<String> hiddenTypes = new HashSet<String>(m_allAdeTypes);
153            hiddenTypes.removeAll(m_includedAdeTypes);
154            for (String typeName : hiddenTypes) {
155                if (OpenCms.getResourceManager().hasResourceType(typeName)) {
156                    additionalTypeMap.put(typeName, OpenCms.getResourceManager().getResourceType(typeName));
157                }
158            }
159        }
160        additionalTypes.addAll(additionalTypeMap.values());
161
162        return internalGetResourceTypesFromConfig(
163            cms,
164            folderRootPath,
165            checkViewableReferenceUri,
166            elementView,
167            additionalTypes,
168            checkEnabled);
169
170    }
171
172    /**
173     * Precomputes type lists for multiple views.<p>
174     *
175     * @param cms the CMS context
176     * @param folderRootPath the current folder
177     * @param checkViewableReferenceUri the reference uri to use for viewability check
178     * @param views the views for which to generate the type lists
179     * @param check object to check whether resource types should be enabled
180     */
181    public void precomputeTypeLists(
182        CmsObject cms,
183        String folderRootPath,
184        String checkViewableReferenceUri,
185        List<CmsElementView> views,
186        I_CmsResourceTypeEnabledCheck check) {
187
188        Multimap<CmsUUID, CmsResourceTypeBean> result = ArrayListMultimap.create();
189
190        // Sort list to make sure that 'Other types' view is processed last, because we may need to display
191        // types filtered / removed from other views, which we only know once we have processed these views
192        Collections.sort(views, new Comparator<CmsElementView>() {
193
194            public int compare(CmsElementView view0, CmsElementView view1) {
195
196                return ComparisonChain.start().compareFalseFirst(view0.isOther(), view1.isOther()).result();
197            }
198        });
199
200        for (CmsElementView view : views) {
201            try {
202                result.putAll(
203                    view.getId(),
204                    getResourceTypes(cms, folderRootPath, checkViewableReferenceUri, view, check));
205            } catch (Exception e) {
206                LOG.error(e.getLocalizedMessage(), e);
207            }
208        }
209        m_cachedTypes = result;
210    }
211
212    /**
213     * Function used to check if a given resource type should be excluded from the result.<p>
214     *
215     * @param type  the type
216     *
217     * @return true if the given type should be excluded
218     */
219    protected boolean exclude(CmsResourceTypeBean type) {
220
221        return false;
222    }
223
224    /**
225     * Creates list of resource type beans for gallery or 'New' dialog.<p>
226     *
227     * @param cms the CMS context
228     * @param folderRootPath the current folder
229     * @param checkViewableReferenceUri the reference uri to use for viewability check
230     * @param elementView  the view id
231     * @param additionalTypes the additional types to add
232     * @param checkEnabled object to check whether resource types should be enabled
233     *
234     * @return the list of resource type beans
235     *
236     * @throws CmsException if something goes wrong
237     */
238    private List<CmsResourceTypeBean> internalGetResourceTypesFromConfig(
239        CmsObject cms,
240        String folderRootPath,
241        String checkViewableReferenceUri,
242        CmsElementView elementView,
243        List<I_CmsResourceType> additionalTypes,
244        I_CmsResourceTypeEnabledCheck checkEnabled)
245    throws CmsException {
246
247        CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, folderRootPath);
248        // String uri = cms.getRequestContext().removeSiteRoot(rootFolder);
249        List<I_CmsResourceType> resourceTypes = new ArrayList<I_CmsResourceType>();
250        Set<String> disabledTypes = new HashSet<String>();
251        final Set<String> typesAtTheEndOfTheList = Sets.newHashSet();
252        Set<String> typesFromConfig = Sets.newHashSet();
253        Map<String, String> createPaths = Maps.newHashMap();
254        Map<String, String> namePatterns = Maps.newHashMap();
255        for (CmsResourceTypeConfig typeConfig : config.getResourceTypes()) {
256            m_allAdeTypes.add(typeConfig.getTypeName());
257            typesFromConfig.add(typeConfig.getTypeName());
258            boolean isModelGroup = CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME.equals(
259                typeConfig.getTypeName());
260            try {
261                AddMenuVisibility visibility = typeConfig.getAddMenuVisibility(elementView.getId(), m_menuType);
262
263                if (visibility == AddMenuVisibility.disabled) {
264                    continue;
265                }
266
267                if (isModelGroup || (visibility == AddMenuVisibility.fromOtherView)) {
268                    typesAtTheEndOfTheList.add(typeConfig.getTypeName());
269                }
270                if (typeConfig.checkViewable(cms, checkViewableReferenceUri)) {
271                    String typeName = typeConfig.getTypeName();
272                    I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(typeName);
273                    resourceTypes.add(resType);
274                    if ((checkEnabled != null) && !checkEnabled.checkEnabled(cms, config, resType)) {
275                        disabledTypes.add(resType.getTypeName());
276                    }
277                }
278            } catch (Exception e) {
279                LOG.error(e.getLocalizedMessage(), e);
280            }
281        }
282        Set<String> creatableTypes = new HashSet<String>();
283        for (CmsResourceTypeConfig typeConfig : config.getCreatableTypes(cms, folderRootPath)) {
284            AddMenuVisibility visibility = typeConfig.getAddMenuVisibility(elementView.getId(), m_menuType);
285            if ((AddMenuVisibility.disabled == visibility)
286                || (AddMenuVisibility.createDisabled == visibility)
287                || disabledTypes.contains(typeConfig.getTypeName())) {
288                continue;
289            }
290            createPaths.put(typeConfig.getTypeName(), typeConfig.getFolderPath(cms, folderRootPath));
291            namePatterns.put(typeConfig.getTypeName(), typeConfig.getNamePattern(false));
292            String typeName = typeConfig.getTypeName();
293            creatableTypes.add(typeName);
294        }
295        m_includedAdeTypes.addAll(createPaths.keySet());
296        CmsGalleryService srv = new CmsGalleryService();
297        srv.setCms(cms);
298        // we put the types 'imported' from other views at the end of the list. Since the sort is stable,
299        // relative position of other types remains unchanged
300        Collections.sort(resourceTypes, new Comparator<I_CmsResourceType>() {
301
302            public int compare(I_CmsResourceType first, I_CmsResourceType second) {
303
304                return ComparisonChain.start().compare(rank(first), rank(second)).result();
305            }
306
307            int rank(I_CmsResourceType type) {
308
309                return typesAtTheEndOfTheList.contains(type.getTypeName()) ? 1 : 0;
310            }
311        });
312
313        Collections.sort(additionalTypes, new Comparator<I_CmsResourceType>() {
314
315            public int compare(I_CmsResourceType type1, I_CmsResourceType type2) {
316
317                CmsExplorerTypeSettings settings1 = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
318                    type1.getTypeName());
319                CmsExplorerTypeSettings settings2 = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
320                    type2.getTypeName());
321                return ComparisonChain.start().compare(
322                    settings1.getViewOrder(true),
323                    settings2.getViewOrder(true)).compare(
324                        parse(settings1.getNewResourceOrder()),
325                        parse(settings2.getNewResourceOrder())).result();
326
327            }
328
329            long parse(String order) {
330
331                try {
332                    return Integer.parseInt(order);
333                } catch (NumberFormatException e) {
334                    return 9999;
335                }
336            }
337        });
338        for (I_CmsResourceType addType : additionalTypes) {
339            String typeName = addType.getTypeName();
340            if (typesFromConfig.contains(typeName) && !elementView.isOther()) { //  type was already processed (although it may not be in the result list)
341                continue;
342            }
343            CmsExplorerTypeSettings explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(typeName);
344            CmsPermissionSet permissions = explorerType.getAccess().getPermissions(
345                cms,
346                cms.readResource(checkViewableReferenceUri));
347            if (permissions.requiresControlPermission() && permissions.requiresViewPermission()) {
348                resourceTypes.add(addType);
349                creatableTypes.add(addType.getTypeName());
350            }
351        }
352        List<CmsResourceTypeBean> results = srv.buildTypesList(resourceTypes, creatableTypes, disabledTypes, null);
353        for (CmsResourceTypeBean typeBean : results) {
354            if (typesFromConfig.contains(typeBean.getType())) {
355                typeBean.setOrigin(Origin.config);
356                typeBean.setCreatePath(createPaths.get(typeBean.getType()));
357                typeBean.setNamePattern(namePatterns.get(typeBean.getType()));
358            } else {
359                typeBean.setOrigin(Origin.other);
360            }
361        }
362
363        List<CmsResourceTypeBean> filteredResults = Lists.newArrayList();
364        for (CmsResourceTypeBean result : results) {
365            if (exclude(result)) {
366                continue;
367            }
368            filteredResults.add(result);
369        }
370
371        return filteredResults;
372
373    }
374
375}