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