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<Map<String, Object>>()); 103 result.put("noright-pages", new ArrayList<Map<String, Object>>()); 104 result.put("allright-pages", new ArrayList<Map<String, Object>>()); 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}