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