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.web.clientsideelement;
017
018import java.util.ArrayList;
019import java.util.Comparator;
020import java.util.HashMap;
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.ConfigurationException;
029import org.apache.avalon.framework.configuration.DefaultConfiguration;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.commons.lang.StringUtils;
033
034import org.ametys.core.right.RightManager.RightResult;
035import org.ametys.core.ui.Callable;
036import org.ametys.core.ui.StaticClientSideElement;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.util.I18nUtils;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.runtime.i18n.I18nizableText;
041import org.ametys.web.repository.page.ModifiablePage;
042import org.ametys.web.repository.page.Page;
043import org.ametys.web.repository.page.ServicesAssignmentHandler;
044import org.ametys.web.service.Service;
045import org.ametys.web.service.ServiceExtensionPoint;
046
047/**
048 * Menu that lists the services
049 */
050public class ServiceMenu extends AbstractPageMenu
051{
052    /** The list of content types */
053    protected ServiceExtensionPoint _serviceExtensionPoint;
054    /** The service assignment handler */
055    protected ServicesAssignmentHandler _servicesHandler;
056    /** The i18n utils */
057    protected I18nUtils _i18nUtils;
058    
059    private boolean _servicesInitialized;
060    
061    @Override
062    public void service(ServiceManager smanager) throws ServiceException
063    {
064        super.service(smanager);
065        
066        _serviceExtensionPoint = (ServiceExtensionPoint) smanager.lookup(ServiceExtensionPoint.ROLE);
067        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
068        _servicesHandler = (ServicesAssignmentHandler) smanager.lookup(ServicesAssignmentHandler.ROLE);
069        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
070    }
071    
072    @Override
073    protected Script _configureScript(Configuration configuration) throws ConfigurationException
074    {
075        Script script = super._configureScript(configuration);
076        
077        for (String serviceId : _serviceExtensionPoint.getExtensionsIds())
078        {
079            Service service = _serviceExtensionPoint.getExtension(serviceId);
080            
081            script.getCSSFiles().addAll(service.getCSSFiles());
082            script.getCSSFiles().addAll(service.getParametersScript().getCSSFiles());
083            script.getScriptFiles().addAll(service.getParametersScript().getScriptFiles());
084        }
085        
086        return script;
087    }
088    
089    @Override
090    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
091    {
092        if (_serviceExtensionPoint.getExtensionsIds().size() == 0)
093        {
094            // Hide menu if there is no service
095            return new ArrayList<>();
096        }
097
098        return super.getScripts(ignoreRights, contextParameters);
099    }
100    
101    /**
102     * Get the available templates for pages
103     * @param pageId The id of the page
104     * @param zoneName the name of the zone
105     * @return the list of templates' name
106     */
107    @Callable
108    public Map<String, Object> getAvailableServices (String pageId, String zoneName)
109    {
110        Map<String, Object> result = new HashMap<>();
111        
112        List<String> services = new ArrayList<>();
113        
114        Page page = _resolver.resolveById(pageId);
115        
116        if (!(page instanceof ModifiablePage))
117        {
118            Map<String, Object> pageParams = getPageDefaultParameters(page);
119            pageParams.put("description", getNoModifiablePageDescription(page));
120
121            result.put("nomodifiable-page", pageParams);
122        }
123        else if (!hasRight(page))
124        {
125            Map<String, Object> pageParams = getPageDefaultParameters(page);
126            pageParams.put("description", getNoRightPageDescription(page));
127
128            @SuppressWarnings("unchecked")
129            List<Map<String, Object>> norightPages = (List<Map<String, Object>>) result.get("noright-pages");
130            norightPages.add(pageParams);
131        }
132        else
133        {
134            Set<String> availablesServicesId = _servicesHandler.getAvailableServices(page, zoneName);
135            for (String serviceId : availablesServicesId)
136            {
137                Service service = _serviceExtensionPoint.getExtension(serviceId);
138                if (hasRight(service, page))
139                {
140                    services.add(serviceId);
141                }
142                
143            }
144        }
145        
146        result.put("services", services);
147        return result;
148    }
149    
150    /**
151     * Determines if user has convenient right on page
152     * @param page The page
153     * @param rightId The right to check
154     * @return true if the user has convenient right
155     */
156    protected boolean _hasRight (Page page, String rightId)
157    {
158        if (StringUtils.isEmpty(rightId))
159        {
160            return true;
161        }
162
163        return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, page) == RightResult.RIGHT_ALLOW;
164    }
165    
166    @Override
167    protected void _getGalleryItems(Map<String, Object> parameters, Map<String, Object> contextualParameters)
168    {
169        try
170        {
171            _lazyInitializeServiceGallery();
172        }
173        catch (Exception e)
174        {
175            throw new IllegalStateException("Unable to lookup client side element local components", e);
176        }
177        
178        super._getGalleryItems(parameters, contextualParameters);
179    }
180    
181    private void _lazyInitializeServiceGallery() throws ConfigurationException
182    {
183        if (!_servicesInitialized)
184        {
185            Map<I18nizableText, Set<Service>> servicesByGroup = _getServicesByGroup();
186            
187            if (servicesByGroup.size() > 0)
188            {
189                GalleryItem galleryItem = new GalleryItem();
190                
191                for (I18nizableText groupLabel : servicesByGroup.keySet())
192                {
193                    GalleryGroup galleryGroup = new GalleryGroup(groupLabel);
194                    galleryItem.addGroup(galleryGroup);
195                    
196                    Set<Service> cTypes = servicesByGroup.get(groupLabel);
197                    for (Service service : cTypes)
198                    {
199                        String id = this.getId() + "-" + service.getId();
200                        
201                        Configuration conf = _getServiceConfiguration (id, service);
202                        _galleryItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf);
203                        galleryGroup.addItem(new UnresolvedItem(id, true));
204                    }
205                }
206                
207                _galleryItems.add(galleryItem);
208            }
209            
210            if (_galleryItems.size() > 0)
211            {
212                try
213                {
214                    _galleryItemManager.initialize();
215                }
216                catch (Exception e)
217                {
218                    throw new ConfigurationException("Unable to lookup parameter local components", e);
219                }
220            }
221        }
222        
223        _servicesInitialized = true;
224    }
225    
226    /**
227     * Get the list of services classified by groups
228     * @return The content types
229     */
230    protected Map<I18nizableText, Set<Service>> _getServicesByGroup ()
231    {
232        Map<I18nizableText, Set<Service>> groups = new TreeMap<>(new I18nizableTextComparator());
233        
234        if (this._script.getParameters().get("services") != null)
235        {
236            String[] serviceIds = ((String) this._script.getParameters().get("services")).split(",");
237            
238            for (String serviceId : serviceIds)
239            {
240                Service service = _serviceExtensionPoint.getExtension(serviceId);
241                
242                if (isValidService(service))
243                {
244                    addService (service, groups);
245                }
246            }
247        }
248        else
249        {
250            Set<String> serviceIds = _serviceExtensionPoint.getExtensionsIds();
251            
252            for (String serviceId : serviceIds)
253            {
254                Service service = _serviceExtensionPoint.getExtension(serviceId);
255                
256                if (isValidService(service))
257                {
258                    addService (service, groups);
259                }
260            }
261        }
262        
263        return groups;
264    }
265    
266    /**
267     * Add service to groups
268     * @param service The service
269     * @param groups The groups
270     */
271    protected void addService (Service service, Map<I18nizableText, Set<Service>> groups)
272    {
273        I18nizableText group = service.getCategory();
274        if ((group.isI18n() && group.getKey().isEmpty()) || (!group.isI18n() && group.getLabel().isEmpty()))
275        {
276            group = new I18nizableText("plugin.web", "PLUGINS_WEB_SERVICE_CATEGORY_90_OTHERS");
277        }
278        
279        if (!groups.containsKey(group))
280        {
281            groups.put(group, new TreeSet<>(new ServiceComparator()));
282        }
283        
284        Set<Service> services = groups.get(group);
285        services.add(service);
286    }
287    
288    
289    /**
290     * Determines if the service is a valid service for the gallery
291     * @param service The service
292     * @return true if it is a valid service
293     */
294    protected boolean isValidService (Service service)
295    {
296        return !service.isPrivate();
297    }
298    
299    /**
300     * Get the configuration of the service item
301     * @param id The id of item
302     * @param service The service
303     * @return The configuration
304     */
305    protected Configuration _getServiceConfiguration (String id, Service service)
306    {
307        DefaultConfiguration conf = new DefaultConfiguration("extension");
308        conf.setAttribute("id", id);
309        
310        DefaultConfiguration classConf = new DefaultConfiguration("class");
311        classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController");
312        
313        // Label
314        DefaultConfiguration labelConf = new DefaultConfiguration("label");
315        I18nizableText label = service.getLabel();
316        if (label.isI18n())
317        {
318            labelConf.setAttribute("i18n", "true");
319            labelConf.setValue(label.getCatalogue() + ":" + label.getKey());
320        }
321        else
322        {
323            labelConf.setValue(label.getLabel());
324        }
325        classConf.addChild(labelConf);
326        
327        // Description
328        DefaultConfiguration descConf = new DefaultConfiguration("description");
329        I18nizableText description = service.getLabel();
330        if (description.isI18n())
331        {
332            descConf.setAttribute("i18n", "true");
333            descConf.setValue(description.getCatalogue() + ":" + description.getKey());
334        }
335        else
336        {
337            descConf.setValue(description.getLabel());
338        }
339        classConf.addChild(descConf);
340        
341        // Service id
342        DefaultConfiguration idConf = new DefaultConfiguration("serviceId");
343        idConf.setValue(service.getId());
344        classConf.addChild(idConf);
345        
346        // Icons or glyph
347        if (service.getIconGlyph() != null)
348        {
349            DefaultConfiguration iconGlyphConf = new DefaultConfiguration("icon-glyph");
350            iconGlyphConf.setValue(service.getIconGlyph());
351            classConf.addChild(iconGlyphConf);
352        }
353        if (service.getIconDecorator() != null)
354        {
355            DefaultConfiguration iconDecoratorConf = new DefaultConfiguration("icon-decorator");
356            iconDecoratorConf.setValue(service.getIconDecorator());
357            classConf.addChild(iconDecoratorConf);
358        }
359        if (service.getSmallIcon() != null)
360        {
361            DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small");
362            iconSmallConf.setValue(service.getSmallIcon());
363            classConf.addChild(iconSmallConf);
364            DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium");
365            iconMediumConf.setValue(service.getMediumIcon());
366            classConf.addChild(iconMediumConf);
367            DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large");
368            iconLargeConf.setValue(service.getLargeIcon());
369            classConf.addChild(iconLargeConf);
370        }
371        
372        // Parameters action
373        DefaultConfiguration paramsAction = new DefaultConfiguration("params-action");
374        paramsAction.setValue(service.getParametersScript().getScriptClassname());
375        classConf.addChild(paramsAction);
376        
377        // Common configuration
378        @SuppressWarnings("unchecked")
379        Map<String, Object> commonConfig = (Map<String, Object>) this._script.getParameters().get("items-config");
380        for (String tagName : commonConfig.keySet())
381        {
382            DefaultConfiguration c = new DefaultConfiguration(tagName);
383            c.setValue(String.valueOf(commonConfig.get(tagName)));
384            classConf.addChild(c);
385        }
386        
387        conf.addChild(classConf);
388        return conf;
389    }
390    
391    class ServiceComparator implements Comparator<Service>
392    {
393        @Override
394        public int compare(Service s1, Service s2)
395        {
396            I18nizableText t1 = s1.getLabel();
397            I18nizableText t2 = s2.getLabel();
398            
399            String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel();
400            String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel();
401            
402            int compareTo = str1.toString().compareTo(str2.toString());
403            if (compareTo == 0)
404            {
405                // Content types have same keys but there are not equals, so do not return 0 to add it in TreeSet
406                // Indeed, in a TreeSet implementation two elements that are equal by the method compareTo are, from the standpoint of the set, equal 
407                return 1;
408            }
409            return compareTo;
410        }
411    }
412    
413    class I18nizableTextComparator implements Comparator<I18nizableText>
414    {
415        @Override
416        public int compare(I18nizableText t1, I18nizableText t2)
417        {
418            String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel();
419            String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel();
420            
421            return str1.toString().compareTo(str2.toString());
422        }
423    }
424    
425    
426    /**
427     * Test if the current user has the right needed by the service to create it.
428     * @param service the service.
429     * @param page the current page.
430     * @return true if the user has the right needed, false otherwise.
431     */
432    protected boolean hasRight(Service service, Page page)
433    {
434        boolean hasRight = false;
435        
436        if (service != null)
437        {
438            String right = service.getRight();
439            
440            if (right == null)
441            {
442                hasRight = true;
443            }
444            else
445            {
446                UserIdentity user = _currentUserProvider.getUser();
447                hasRight = _rightManager.hasRight(user, right, page) == RightResult.RIGHT_ALLOW;
448            }
449        }
450        
451        return hasRight;
452    }
453    
454}