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}