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.plugins.newsletter.category;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
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.cocoon.components.LifecycleHelper;
033import org.apache.cocoon.util.log.SLF4JLoggerAdapter;
034import org.apache.excalibur.source.SourceNotFoundException;
035import org.apache.excalibur.source.SourceResolver;
036import org.apache.excalibur.source.TraversableSource;
037import org.slf4j.LoggerFactory;
038
039import org.ametys.core.ui.Callable;
040import org.ametys.core.ui.SimpleMenu;
041import org.ametys.core.ui.StaticClientSideElement;
042import org.ametys.core.util.I18nUtils;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.runtime.plugin.component.ThreadSafeComponentManager;
046import org.ametys.web.repository.site.Site;
047import org.ametys.web.repository.site.SiteManager;
048import org.ametys.web.skin.Skin;
049import org.ametys.web.skin.SkinsManager;
050
051/**
052 * This element finally creates a gallery button with one item per template  
053 */
054public class TemplatesMenu extends SimpleMenu
055{
056    /** The plugin in which are the templates resources (messages and icons). */
057    protected static final String _RESOURCES_PLUGIN = "newsletter";
058    
059    /** The skins manager */
060    protected SkinsManager _skinsManager;
061    /** The site manager */
062    protected SiteManager _siteManager;
063    /** The source resolver */
064    protected SourceResolver _sourceResolver;
065    /** The service manager */
066    protected ServiceManager _manager;
067    /** The i18n utils */
068    protected I18nUtils _i18nUtils;
069    /** The Ametys object resolver */
070    protected AmetysObjectResolver _resolver;
071    
072    @Override
073    public void service(ServiceManager smanager) throws ServiceException
074    {
075        super.service(smanager);
076        _manager = smanager;
077        _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE);
078        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
079        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
080        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
081        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
082    }
083    
084    /**
085     * Get the available templates for pages
086     * @param categoryIds The ids of categories
087     * @return the list of templates' name
088     */
089    @Callable
090    public Map<String, Object> getStatus (List<String> categoryIds)
091    {
092        Map<String, Object> result = new HashMap<>();
093        
094        result.put("categories", new ArrayList<Map<String, Object>>());
095        
096        for (String categoryId : categoryIds)
097        {
098            JCRCategory category = _resolver.resolveById(categoryId);
099            
100            // Category parameters
101            Map<String, Object> categoryParams = getCategoryDefaultParameters(category);
102            categoryParams.put("template", category.getTemplate());
103            
104            @SuppressWarnings("unchecked")
105            List<Map<String, Object>> categories = (List<Map<String, Object>>) result.get("categories");
106            categories.add(categoryParams);
107        }
108        
109        return result;
110    }
111    
112    /**
113     * Get the default category's parameters
114     * @param category The category
115     * @return The default parameters
116     */
117    protected Map<String, Object> getCategoryDefaultParameters (JCRCategory category)
118    {
119        Map<String, Object> categoryParams = new HashMap<>();
120        categoryParams.put("id", category.getId());
121        categoryParams.put("title", category.getTitle());
122        
123        return categoryParams;
124    }
125    
126    @Override
127    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
128    {
129        String siteName = (String) contextParameters.get("siteName");
130        
131        String skinId = _siteManager.getSite(siteName).getSkinId();
132        Skin skin = _skinsManager.getSkin(skinId);
133        
134        if (skin.getTemplates().size() == 0)
135        {
136            // Hide menu if there is no template
137            return new ArrayList<>();
138        }
139            
140        return super.getScripts(ignoreRights, contextParameters);
141    }
142
143    /**
144     * Get templates of newletter categories
145     * @param categoryIds the ids of categories
146     * @return The list of used templates
147     */
148    @Callable
149    public List<String> getTemplate (List<String> categoryIds)
150    {
151        List<String> templates = new ArrayList<>();
152        
153        for (String id : categoryIds)
154        {
155            Category category = _resolver.resolveById(id);
156            
157            if (!templates.contains(category.getTemplate()))
158            {
159                templates.add(category.getTemplate());
160            }
161        }
162        return templates;
163    }
164    
165    @Override
166    protected void _getGalleryItems(Map<String, Object> parameters, Map<String, Object> contextualParameters)
167    {
168        try
169        {
170            _lazyInitializeTemplateGallery(contextualParameters);
171        }
172        catch (Exception e)
173        {
174            throw new IllegalStateException("Unable to lookup client side element local components", e);
175        }
176        
177        super._getGalleryItems(parameters, contextualParameters);
178    }
179    
180    private void _lazyInitializeTemplateGallery(Map<String, Object> contextualParameters) throws ConfigurationException
181    {
182        String siteName = (String) contextualParameters.get("siteName");
183        
184        // We need to dispose and use a new manager each time as items depends on the current site
185        // FIXME make a smart cache by skin ? The cache has to be clear when the skin is updated
186        _galleryItemManager.dispose();
187        
188        _galleryItemManager = new ThreadSafeComponentManager<>();
189        _galleryItemManager.setLogger(LoggerFactory.getLogger("cms.plugin.threadsafecomponent"));
190        _galleryItemManager.service(_smanager);
191    
192        Site site = _siteManager.getSite(siteName);
193        String skinId = site.getSkinId();
194        
195        Set<String> templatesId = new HashSet<>();
196        TraversableSource dir;
197        try
198        {
199            dir = (TraversableSource) _sourceResolver.resolveURI("context://skins/" + skinId + "/newsletter");
200            if (dir.exists())
201            {                
202                for (TraversableSource child : (Collection<TraversableSource>) dir.getChildren())
203                {
204                    if (_templateExists(skinId, child.getName()))
205                    {
206                        templatesId.add(child.getName());
207                    }
208                }
209            }
210        }
211        catch (SourceNotFoundException e)
212        {
213            // Silently ignore
214        }
215        catch (Exception e)
216        {
217            throw new ConfigurationException("Cannot find template for the skin " + skinId, e);
218        }
219        
220        
221        if (!templatesId.isEmpty())
222        {
223            GalleryItem galleryItem = new GalleryItem();
224            
225            GalleryGroup galleryGroup = new GalleryGroup(new I18nizableText("plugin." + _RESOURCES_PLUGIN, "PLUGINS_NEWSLETTER_CATEGORY_TEMPLATESMENU_GROUP_LABEL"));
226            galleryItem.addGroup(galleryGroup);
227                
228            for (String templateId : templatesId)
229            {
230                String id = this.getId() + "." + templateId;
231                
232                try
233                {
234                    NewsletterTemplate template = new NewsletterTemplate(skinId, templateId);
235                    LifecycleHelper.setupComponent(template, new SLF4JLoggerAdapter(getLogger()), null, _manager, null, true);
236                    template.refreshValues();
237                    
238                    Configuration conf = _getTemplateItemConfiguration (id, template);
239                    _galleryItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf);
240                    galleryGroup.addItem(new UnresolvedItem(id, true));
241                }
242                catch (Exception e)
243                {
244                    throw new ConfigurationException("Unable to configure local client side element of id " + id, e);
245                }
246            }
247            
248            _galleryItems.add(galleryItem);
249            
250            if (_galleryItems.size() > 0)
251            {
252                try
253                {
254                    _galleryItemManager.initialize();
255                }
256                catch (Exception e)
257                {
258                    throw new ConfigurationException("Unable to lookup parameter local components", e);
259                }
260            }
261        }
262    }
263    
264    private boolean _templateExists(String skinId, String id) throws IOException
265    {
266        TraversableSource skinsDir = (TraversableSource) _sourceResolver.resolveURI("context://skins/" + skinId + "/newsletter/" + id + "/stylesheets/template.xsl");
267        return skinsDir.exists();
268    }
269    
270    /**
271     * Get the configuration for a template item
272     * @param itemId The item id
273     * @param template The newsletter template
274     * @return The configuration
275     */
276    protected Configuration _getTemplateItemConfiguration (String itemId, NewsletterTemplate template)
277    {
278        DefaultConfiguration conf = new DefaultConfiguration("extension");
279        conf.setAttribute("id", itemId);
280        
281        DefaultConfiguration classConf = new DefaultConfiguration("class");
282        classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController");
283        
284        // Label
285        DefaultConfiguration labelConf = new DefaultConfiguration("label");
286        labelConf.setValue(_i18nUtils.translate(template.getLabel()));
287        classConf.addChild(labelConf);
288        
289        // Description
290        DefaultConfiguration descConf = new DefaultConfiguration("description");
291        descConf.setValue(_i18nUtils.translate(template.getDescription()));
292        classConf.addChild(descConf);
293        
294        // Name
295        DefaultConfiguration nameConf = new DefaultConfiguration("name");
296        nameConf.setValue(template.getId());
297        classConf.addChild(nameConf);
298        
299        // Icons
300        DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small");
301        iconSmallConf.setValue(template.getSmallImage());
302        classConf.addChild(iconSmallConf);
303        DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium");
304        iconMediumConf.setValue(template.getMediumImage());
305        classConf.addChild(iconMediumConf);
306        DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large");
307        iconLargeConf.setValue(template.getLargeImage());
308        classConf.addChild(iconLargeConf);
309        
310     // Common configuration
311        @SuppressWarnings("unchecked")
312        Map<String, Object> commonConfig = (Map<String, Object>) this._script.getParameters().get("items-config");
313        for (String tagName : commonConfig.keySet())
314        {
315            DefaultConfiguration c = new DefaultConfiguration(tagName);
316            c.setValue(String.valueOf(commonConfig.get(tagName)));
317            classConf.addChild(c);
318        }
319        
320        conf.addChild(classConf);
321        return conf;
322    }
323}