001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.clientsideelement;
017
018import java.util.ArrayList;
019import java.util.Comparator;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024import java.util.TreeMap;
025import java.util.TreeSet;
026
027import org.apache.avalon.framework.configuration.Configuration;
028import org.apache.avalon.framework.configuration.DefaultConfiguration;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031
032import org.ametys.cms.content.RootContentHelper;
033import org.ametys.cms.contenttype.ContentType;
034import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
035import org.ametys.core.right.RightManager.RightResult;
036import org.ametys.core.ui.SimpleMenu;
037import org.ametys.core.ui.StaticClientSideElement;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.core.util.I18nUtils;
040import org.ametys.core.util.I18nizableTextKeyComparator;
041import org.ametys.runtime.i18n.I18nizableText;
042
043/**
044 * This element creates a menu item with one menu item per content type. 
045 * The user rights are checked. 
046 */
047public class ContentTypesMenuItem extends SimpleMenu
048{
049    /** The list of content types */
050    protected ContentTypeExtensionPoint _contentTypeExtensionPoint;
051    /** The i18n utils */
052    protected I18nUtils _i18nUtils;
053    /** Helper for root content */
054    protected RootContentHelper _rootContentHelper;
055    
056    private boolean _contentTypesInitialized;
057    
058    private Set<String> _addedComponents;
059    
060    @Override
061    public void service(ServiceManager smanager) throws ServiceException
062    {
063        super.service(smanager);
064        _contentTypeExtensionPoint = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
065        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
066        _rootContentHelper = (RootContentHelper) smanager.lookup(RootContentHelper.ROLE);
067    }
068    
069    private synchronized void _lazyInitializeContentTypeMenu()
070    {
071        if (!_contentTypesInitialized)
072        {
073            _addedComponents = new HashSet<>();
074            
075            Map<I18nizableText, Set<ContentType>> cTypesByGroup = _getContentTypesByGroup();
076            
077            if (cTypesByGroup.size() > 0)
078            {
079                for (I18nizableText groupLabel : cTypesByGroup.keySet())
080                {
081                    Set<ContentType> cTypes = cTypesByGroup.get(groupLabel);
082                    for (ContentType cType : cTypes)
083                    {
084                        String id = this.getId() + "-" + cType.getId();
085                        
086                        Configuration conf = _getContentTypeConfiguration (id, cType);
087                        if (!_addedComponents.contains(id))
088                        {
089                            _menuItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf);
090                            _unresolvedMenuItems.add(new UnresolvedItem(id, true));
091                            _addedComponents.add(id);
092                        }
093                    }
094                }
095            }
096        }
097        
098        _contentTypesInitialized = true;
099    }
100    
101    /**
102     * Get the configuration of the content type item
103     * @param id The id of item
104     * @param cType The content type
105     * @return The configuration
106     */
107    protected Configuration _getContentTypeConfiguration (String id, ContentType cType)
108    {
109        DefaultConfiguration conf = new DefaultConfiguration("extension");
110        conf.setAttribute("id", id);
111        
112        DefaultConfiguration classConf = new DefaultConfiguration("class");
113        classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController");
114        
115        // Label
116        DefaultConfiguration labelConf = new DefaultConfiguration("label");
117        labelConf.setValue(_i18nUtils.translate(cType.getLabel()));
118        classConf.addChild(labelConf);
119        
120        // Description
121        DefaultConfiguration descConf = new DefaultConfiguration("description");
122        descConf.setValue(_i18nUtils.translate(cType.getDescription()));
123        classConf.addChild(descConf);
124        
125        // Content type
126        DefaultConfiguration typeConf = new DefaultConfiguration("contentTypes");
127        typeConf.setValue(cType.getId());
128        classConf.addChild(typeConf);
129        
130        // Icons or glyph
131        if (cType.getIconGlyph() != null)
132        {
133            DefaultConfiguration iconGlyphConf = new DefaultConfiguration("icon-glyph");
134            iconGlyphConf.setValue(cType.getIconGlyph());
135            classConf.addChild(iconGlyphConf);
136        }
137        if (cType.getIconDecorator() != null)
138        {
139            DefaultConfiguration iconDecoratorConf = new DefaultConfiguration("icon-decorator");
140            iconDecoratorConf.setValue(cType.getIconDecorator());
141            classConf.addChild(iconDecoratorConf);
142        }
143        if (cType.getSmallIcon() != null)
144        {
145            DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small");
146            iconSmallConf.setValue(cType.getSmallIcon());
147            classConf.addChild(iconSmallConf);
148            DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium");
149            iconMediumConf.setValue(cType.getMediumIcon());
150            classConf.addChild(iconMediumConf);
151            DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large");
152            iconLargeConf.setValue(cType.getLargeIcon());
153            classConf.addChild(iconLargeConf);
154        }
155        
156        // Common configuration
157        @SuppressWarnings("unchecked")
158        Map<String, Object> commonConfig = (Map<String, Object>) this._script.getParameters().get("items-config");
159        for (String tagName : commonConfig.keySet())
160        {
161            DefaultConfiguration c = new DefaultConfiguration(tagName);
162            c.setValue(String.valueOf(commonConfig.get(tagName)));
163            classConf.addChild(c);
164        }
165        
166        conf.addChild(classConf);
167        return conf;
168    }
169    
170    /**
171     * Get the list of content types classified by groups
172     * @return The content types
173     */
174    protected Map<I18nizableText, Set<ContentType>> _getContentTypesByGroup ()
175    {
176        Map<I18nizableText, Set<ContentType>> groups = new TreeMap<>(new I18nizableTextKeyComparator());
177        
178        if (this._script.getParameters().get("contentTypes") != null)
179        {
180            String[] contentTypesIds = ((String) this._script.getParameters().get("contentTypes")).split(",");
181            
182            boolean allowInherit = "true".equals(this._script.getParameters().get("allowInherit"));
183            
184            for (String contentTypeId : contentTypesIds)
185            {
186                ContentType contentType = _contentTypeExtensionPoint.getExtension(contentTypeId);
187                
188                if (isValidContentType(contentType))
189                {
190                    addContentType (contentType, groups);
191                }
192                
193                if (allowInherit)
194                {
195                    for (String subTypeId : _contentTypeExtensionPoint.getSubTypes(contentTypeId))
196                    {
197                        ContentType subContentType = _contentTypeExtensionPoint.getExtension(subTypeId);
198                        if (isValidContentType(subContentType))
199                        {
200                            addContentType (subContentType, groups);
201                        }
202                    }
203                }
204            }
205        }
206        else
207        {
208            Set<String> contentTypesIds = _contentTypeExtensionPoint.getExtensionsIds();
209            
210            for (String contentTypeId : contentTypesIds)
211            {
212                ContentType contentType = _contentTypeExtensionPoint.getExtension(contentTypeId);
213                
214                if (isValidContentType(contentType))
215                {
216                    addContentType (contentType, groups);
217                }
218            }
219        }
220        
221        return groups;
222    }
223    
224    /**
225     * Add content to groups
226     * @param contentType The content type
227     * @param groups The groups
228     */
229    protected void addContentType (ContentType contentType, Map<I18nizableText, Set<ContentType>> groups)
230    {
231        I18nizableText group = contentType.getCategory();
232        if (group.isI18n() && group.getKey().isEmpty()
233            || !group.isI18n() && group.getLabel().isEmpty())
234        {
235            group = new I18nizableText("plugin.cms", "PLUGINS_CMS_CONTENT_CREATECONTENTMENU_GROUP_10_CONTENT");
236        }
237        
238        if (!groups.containsKey(group))
239        {
240            groups.put(group, new TreeSet<>(new ContentTypeComparator()));
241        }
242        Set<ContentType> cTypes = groups.get(group);
243        cTypes.add(contentType);
244    }
245    
246    @Override
247    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
248    {
249        try
250        {
251            _lazyInitializeContentTypeMenu();
252        }
253        catch (Exception e)
254        {
255            throw new IllegalStateException("Unable to lookup client side element local components", e);
256        }
257        
258        for (String id: _contentTypeExtensionPoint.getExtensionsIds())
259        {
260            ContentType contentType = _contentTypeExtensionPoint.getExtension(id);
261            if (isValidContentType(contentType))
262            {
263                return super.getScripts(ignoreRights, contextParameters);
264            }
265        }
266        return new ArrayList<>();
267    }
268    
269    /**
270     * Determines if the content type is a valid content type for the gallery
271     * @param contentType The coentent
272     * @return true if it is a valid content type
273     */
274    protected boolean isValidContentType (ContentType contentType)
275    {
276        return !contentType.isAbstract() && !contentType.isPrivate() && !contentType.isMixin();
277    }
278    
279    /**
280     * Test if the current user has the right needed by the content type to create a content.
281     * @param cType the content type
282     * @return true if the user has the right needed, false otherwise.
283     */
284    protected boolean hasRight(ContentType cType)
285    {
286        String right = cType.getRight();
287        
288        if (right == null)
289        {
290            return true;
291        }
292        else
293        {
294            UserIdentity user = _currentUserProvider.getUser();
295            return _rightManager.hasRight(user, right, "/cms") == RightResult.RIGHT_ALLOW || _rightManager.hasRight(user, right, _rootContentHelper.getRootContent()) == RightResult.RIGHT_ALLOW;
296        }
297    }
298    
299    class ContentTypeComparator implements Comparator<ContentType>
300    {
301        @Override
302        public int compare(ContentType c1, ContentType c2)
303        {
304            I18nizableText t1 = c1.getLabel();
305            I18nizableText t2 = c2.getLabel();
306            
307            String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel();
308            String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel();
309            
310            int compareTo = str1.toString().compareTo(str2.toString());
311            if (compareTo == 0)
312            {
313                // Content types have same keys but there are not equals, so do not return 0 to add it in TreeSet
314                // Indeed, in a TreeSet implementation two elements that are equal by the method compareTo are, from the standpoint of the set, equal 
315                return 1;
316            }
317            return compareTo;
318        }
319    }
320}