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