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.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfiguration;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.cocoon.components.ContextHelper;
034import org.apache.cocoon.environment.Request;
035import org.slf4j.LoggerFactory;
036
037import org.ametys.core.ui.Callable;
038import org.ametys.core.ui.ClientSideElement;
039import org.ametys.core.ui.StaticClientSideElement;
040import org.ametys.core.util.I18nUtils;
041import org.ametys.plugins.repository.AmetysObjectResolver;
042import org.ametys.runtime.i18n.I18nizableText;
043import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
044import org.ametys.web.repository.page.ModifiablePage;
045import org.ametys.web.repository.page.Page;
046import org.ametys.web.repository.page.Page.PageType;
047import org.ametys.web.repository.site.SiteManager;
048import org.ametys.web.skin.Skin;
049import org.ametys.web.skin.SkinTemplate;
050import org.ametys.web.skin.SkinsManager;
051import org.ametys.web.skin.TemplatesAssignmentHandler;
052
053/**
054 * This element finally creates a gallery button with one item per template  
055 */
056public class TemplatesMenu extends AbstractPageMenu implements Contextualizable
057{
058    /** The plugin in which are the templates resources (messages and icons). */
059    protected static final String _RESOURCES_PLUGIN = "web";
060    
061    private static final String GALLERY_ITEM_MANAGER = TemplatesMenu.class.getName() + "$GalleryItemManager";
062    private static final String GALLERY_ITEMS = TemplatesMenu.class.getName() + "$GalleryItems";
063    
064    /** The skins manager */
065    protected SkinsManager _skinsManager;
066    /** The template assignment handler */
067    protected TemplatesAssignmentHandler _templatesAssignmentHandler;
068    /** The site manager */
069    protected SiteManager _siteManager;
070    /** The i18n utils */
071    protected I18nUtils _i18nUtils;
072    
073    private Context _context;
074    
075    @Override
076    public void service(ServiceManager smanager) throws ServiceException
077    {
078        super.service(smanager);
079        
080        _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE);
081        _templatesAssignmentHandler = (TemplatesAssignmentHandler) smanager.lookup(TemplatesAssignmentHandler.ROLE);
082        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
083        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
084        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
085    }
086    
087    public void contextualize(Context context) throws ContextException
088    {
089        _context = context;
090    }
091    
092    /**
093     * Get the available templates for pages
094     * @param pageIds The ids of pages
095     * @return the list of templates' name
096     */
097    @Callable
098    public Map<String, Object> getStatus (List<String> pageIds)
099    {
100        Map<String, Object> result = new HashMap<>();
101        
102        result.put("nomodifiable-pages", new ArrayList<>());
103        result.put("noright-pages", new ArrayList<>());
104        result.put("allright-pages", new ArrayList<>());
105        
106        Set<String> availableTemplateIds = new HashSet<>();
107        for (String pageId : pageIds)
108        {
109            Page page = _resolver.resolveById(pageId);
110            availableTemplateIds.addAll(_templatesAssignmentHandler.getAvailablesTemplates(page));
111            
112            if (!(page instanceof ModifiablePage))
113            {
114                Map<String, Object> pageParams = getPageDefaultParameters(page);
115                pageParams.put("description", getNoModifiablePageDescription(page));
116
117                @SuppressWarnings("unchecked")
118                List<Map<String, Object>> norightPages = (List<Map<String, Object>>) result.get("nomodifiable-pages");
119                norightPages.add(pageParams);
120            }
121            else if (!hasRight(page))
122            {
123                Map<String, Object> pageParams = getPageDefaultParameters(page);
124                pageParams.put("description", getNoRightPageDescription(page));
125
126                @SuppressWarnings("unchecked")
127                List<Map<String, Object>> norightPages = (List<Map<String, Object>>) result.get("noright-pages");
128                norightPages.add(pageParams);
129            }
130            else
131            {
132                Map<String, Object> pageParams = getPageDefaultParameters(page);
133                pageParams.put("description", getAllRightPageDescription(page));
134                if (page.getType() == PageType.CONTAINER)
135                {
136                    pageParams.put("template", page.getTemplate());
137                }
138                
139                @SuppressWarnings("unchecked")
140                List<Map<String, Object>> allRightPages = (List<Map<String, Object>>) result.get("allright-pages");
141                allRightPages.add(pageParams);
142            }
143        }
144        
145        result.put("templates", new ArrayList<>(availableTemplateIds));
146        return result;
147    }
148    
149    @Override
150    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
151    {
152        String siteName = (String) contextParameters.get("siteName");
153        
154        String skinId = _siteManager.getSite(siteName).getSkinId();
155        Skin skin = _skinsManager.getSkin(skinId);
156        
157        if (skin.getTemplates().size() == 0)
158        {
159            // Hide menu if there is no template
160            return new ArrayList<>();
161        }
162            
163        try
164        {
165            _lazyInitializeTemplateGallery(contextParameters);
166
167            return super.getScripts(ignoreRights, contextParameters);
168        }
169        catch (Exception e)
170        {
171            throw new IllegalStateException("Unable to lookup client side element local components", e);
172        }
173        finally
174        {
175            _getGalleryItemManager().dispose();
176        }
177    }
178    
179    @SuppressWarnings("unchecked")
180    @Override
181    protected ThreadSafeComponentManager<ClientSideElement> _getGalleryItemManager()
182    {
183        Request request = ContextHelper.getRequest(_context);
184        return (ThreadSafeComponentManager<ClientSideElement>) request.getAttribute(GALLERY_ITEM_MANAGER);
185    }
186    
187    @SuppressWarnings("unchecked")
188    @Override
189    protected List<GalleryItem> _getGalleryItems()
190    {
191        Request request = ContextHelper.getRequest(_context);
192        return (List<GalleryItem>) request.getAttribute(GALLERY_ITEMS);
193    }
194    
195    private void _lazyInitializeTemplateGallery(Map<String, Object> contextualParameters) throws ConfigurationException
196    {
197        // FIXME make a smart cache by skin ? The cache has to be cleared when the skin is updated
198        
199        Request request = ContextHelper.getRequest(_context);
200        
201        @SuppressWarnings("unchecked")
202        ThreadSafeComponentManager<ClientSideElement> oldGalleryItemManagerInSameRequest = (ThreadSafeComponentManager<ClientSideElement>) request.getAttribute(GALLERY_ITEM_MANAGER);
203        if (oldGalleryItemManagerInSameRequest != null)
204        {
205            oldGalleryItemManagerInSameRequest.dispose();
206        }
207
208        ThreadSafeComponentManager<ClientSideElement> galleryItemManager = new ThreadSafeComponentManager<>();
209        galleryItemManager.setLogger(LoggerFactory.getLogger("cms.plugin.threadsafecomponent"));
210        galleryItemManager.service(_smanager);
211        request.setAttribute(GALLERY_ITEM_MANAGER, galleryItemManager);
212        
213        List<GalleryItem> galleryItems = new ArrayList<>();
214        request.setAttribute(GALLERY_ITEMS, galleryItems);
215        
216        String siteName = (String) contextualParameters.get("siteName");
217        
218        String skinId = _siteManager.getSite(siteName).getSkinId();
219        Set<String> availablesTemplates = _templatesAssignmentHandler.getAvailablesTemplates(skinId);
220        
221        Skin skin = _skinsManager.getSkin(skinId);
222        
223        if (availablesTemplates.size() > 0)
224        {
225            GalleryItem galleryItem = new GalleryItem();
226            
227            GalleryGroup galleryGroup = new GalleryGroup(new I18nizableText("plugin." + _RESOURCES_PLUGIN, "PLUGINS_WEB_PAGE_TEMPLATESMENU_GROUP_LABEL"));
228            galleryItem.addGroup(galleryGroup);
229                
230            for (String templateId : availablesTemplates)
231            {
232                String id = this.getId() + "." + templateId;
233                
234                SkinTemplate template = skin.getTemplate(templateId);
235                Configuration conf = _getTemplateItemConfiguration (id, template);
236                galleryItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf);
237                galleryGroup.addItem(new UnresolvedItem(id, true));
238            }
239            
240            galleryItems.add(galleryItem);
241        }
242        
243        try
244        {
245            galleryItemManager.initialize();
246        }
247        catch (Exception e)
248        {
249            throw new ConfigurationException("Unable to lookup parameter local components", e);
250        }
251    }
252    
253    /**
254     * Get the configuration for a language item
255     * @param itemId The item id
256     * @param template The template used for the item
257     * @return The configuration
258     */
259    protected Configuration _getTemplateItemConfiguration (String itemId, SkinTemplate template)
260    {
261        DefaultConfiguration conf = new DefaultConfiguration("extension");
262        conf.setAttribute("id", itemId);
263        
264        DefaultConfiguration classConf = new DefaultConfiguration("class");
265        classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController");
266        
267        // Label
268        DefaultConfiguration labelConf = new DefaultConfiguration("label");
269        labelConf.setValue(_i18nUtils.translate(template.getLabel()));
270        classConf.addChild(labelConf);
271        
272        // Description
273        DefaultConfiguration descConf = new DefaultConfiguration("description");
274        descConf.setValue(_i18nUtils.translate(template.getDescription()));
275        classConf.addChild(descConf);
276        
277        // Name
278        DefaultConfiguration nameConf = new DefaultConfiguration("name");
279        nameConf.setValue(template.getId());
280        classConf.addChild(nameConf);
281        
282        // Icons
283        DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small");
284        iconSmallConf.setValue(template.getSmallImage());
285        classConf.addChild(iconSmallConf);
286        DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium");
287        iconMediumConf.setValue(template.getMediumImage());
288        classConf.addChild(iconMediumConf);
289        DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large");
290        iconLargeConf.setValue(template.getLargeImage());
291        classConf.addChild(iconLargeConf);
292        
293        // Toggle button
294        DefaultConfiguration toggleEnabledConf = new DefaultConfiguration("toggle-enabled");
295        toggleEnabledConf.setValue("true");
296        classConf.addChild(toggleEnabledConf);
297        
298        // Common configuration
299        @SuppressWarnings("unchecked")
300        Map<String, Object> commonConfig = (Map<String, Object>) this._script.getParameters().get("items-config");
301        for (String tagName : commonConfig.keySet())
302        {
303            DefaultConfiguration c = new DefaultConfiguration(tagName);
304            c.setValue(String.valueOf(commonConfig.get(tagName)));
305            classConf.addChild(c);
306        }
307        
308        conf.addChild(classConf);
309        return conf;
310    }
311    
312}