001/* 002 * Copyright 2013 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.cms.clientsideelement; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.avalon.framework.component.ComponentException; 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.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.slf4j.LoggerFactory; 031 032import org.ametys.core.ui.ClientSideElement; 033import org.ametys.core.ui.MenuClientSideElement; 034import org.ametys.core.ui.RibbonControlsManager; 035import org.ametys.core.ui.SimpleMenu; 036import org.ametys.core.ui.StaticClientSideElement; 037import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 038import org.ametys.runtime.workspace.WorkspaceMatcher; 039 040/** 041 * A smart menu to use for content 042 * 043 */ 044public class SmartContentMenu extends SmartContentClientSideElement implements MenuClientSideElement 045{ 046 /** The client side element component manager for menu items. */ 047 protected ThreadSafeComponentManager<ClientSideElement> _menuItemManager; 048 /** The ribbon control manager */ 049 protected RibbonControlsManager _ribbonControlManager; 050 /** The service manager. */ 051 protected ServiceManager _serviceManager; 052 053 /** The menu items */ 054 protected List<ClientSideElement> _menuItems; 055 /** The primary menu item */ 056 protected ClientSideElement _primaryMenuItem; 057 058 private List<ClientSideElement> _referencedClientSideElement; 059 private List<UnresolvedItem> _unresolvedMenuItems; 060 061 @Override 062 public void service(ServiceManager serviceManager) throws ServiceException 063 { 064 super.service(serviceManager); 065 _serviceManager = serviceManager; 066 _ribbonControlManager = (RibbonControlsManager) serviceManager.lookup(RibbonControlsManager.ROLE); 067 } 068 069 @Override 070 public void configure(Configuration configuration) throws ConfigurationException 071 { 072 _menuItemManager = new ThreadSafeComponentManager<>(); 073 _menuItemManager.setLogger(LoggerFactory.getLogger("cms.plugin.threadsafecomponent")); 074 _menuItemManager.service(_serviceManager); 075 076 super.configure(configuration); 077 078 // Menu items. 079 _referencedClientSideElement = new ArrayList<>(); 080 _unresolvedMenuItems = new ArrayList<>(); 081 _menuItems = new ArrayList<>(); 082 083 _configureItemsMenu(configuration); 084 } 085 086 /** 087 * Configure the items menu 088 * @param configuration the configuration 089 * @throws ConfigurationException If the configuration is not correct. 090 */ 091 protected void _configureItemsMenu(Configuration configuration) throws ConfigurationException 092 { 093 for (Configuration menuItemConfiguration : configuration.getChildren("menu-items")) 094 { 095 for (Configuration itemConfig : menuItemConfiguration.getChildren("item")) 096 { 097 boolean isPrimary = itemConfig.getAttributeAsBoolean("primaryItem", false); 098 099 if (itemConfig.getAttribute("ref", null) != null) 100 { 101 _unresolvedMenuItems.add(new UnresolvedItem(itemConfig.getAttribute("ref"), false, isPrimary)); 102 } 103 else 104 { 105 String id = itemConfig.getAttribute("id"); 106 107 DefaultConfiguration conf = new DefaultConfiguration("extension"); 108 conf.setAttribute("id", id); 109 conf.addChild(itemConfig.getChild("class")); 110 111 if (itemConfig.getChild("menu-items", false) != null || itemConfig.getChild("gallery-item", false) != null) 112 { 113 if (itemConfig.getChild("menu-items", false) != null) 114 { 115 conf.addChild(itemConfig.getChild("menu-items")); 116 } 117 118 if (itemConfig.getChild("gallery-item", false) != null) 119 { 120 conf.addChild(itemConfig.getChild("gallery-item")); 121 } 122 123 _menuItemManager.addComponent(_pluginName, null, id, SimpleMenu.class, conf); 124 } 125 else 126 { 127 _menuItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf); 128 } 129 _unresolvedMenuItems.add(new UnresolvedItem(id, true, isPrimary)); 130 } 131 } 132 } 133 } 134 135 @Override 136 public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters) 137 { 138 List<Script> result = new ArrayList<>(); 139 140 try 141 { 142 _resolveMenuItems(); 143 } 144 catch (Exception e) 145 { 146 throw new IllegalStateException("Unable to lookup client side element local components", e); 147 } 148 149 Map<String, Object> parameters = new HashMap<>(); 150 List<ScriptFile> cssFiles = new ArrayList<>(); 151 List<ScriptFile> scriptFiles = new ArrayList<>(); 152 153 // FIXME handle rights for workspace admin, here is a temporary workaround 154 if (ignoreRights || (contextParameters != null && "admin".equals(contextParameters.get(WorkspaceMatcher.WORKSPACE_NAME))) || hasRight(getRights(contextParameters))) 155 { 156 List<Script> scripts = super.getScripts(ignoreRights, contextParameters); 157 for (Script script : scripts) 158 { 159 cssFiles.addAll(script.getCSSFiles()); 160 scriptFiles.addAll(script.getScriptFiles()); 161 parameters.putAll(script.getParameters()); 162 } 163 164 if (_primaryMenuItem != null) 165 { 166 for (Script script : _primaryMenuItem.getScripts(ignoreRights, contextParameters)) 167 { 168 Map<String, Object> primaryParameters = script.getParameters(); 169 parameters.put("primary-menu-item-id", script.getId()); 170 for (String paramId : primaryParameters.keySet()) 171 { 172 if (!parameters.containsKey(paramId)) 173 { 174 parameters.put(paramId, primaryParameters.get(paramId)); 175 } 176 } 177 } 178 } 179 180 // Menu items 181 if (_menuItems.size() > 0) 182 { 183 List<String> menuItems = new ArrayList<>(); 184 for (ClientSideElement element : _menuItems) 185 { 186 menuItems.add(element.getId()); 187 } 188 parameters.put("menu-items", menuItems); 189 } 190 191 result.add(new Script(this.getId(), _script.getScriptClassname(), scriptFiles, cssFiles, parameters)); 192 } 193 194 return result; 195 } 196 197 @Override 198 public List<ClientSideElement> getReferencedClientSideElements() 199 { 200 return Collections.unmodifiableList(_referencedClientSideElement); 201 } 202 203 204 private void _resolveMenuItems() throws Exception 205 { 206 if (_unresolvedMenuItems != null) 207 { 208 _menuItemManager.initialize(); 209 210 for (UnresolvedItem unresolvedItem : _unresolvedMenuItems) 211 { 212 String id = unresolvedItem.getId(); 213 ClientSideElement element; 214 if (unresolvedItem.isLocalItem()) 215 { 216 try 217 { 218 element = _menuItemManager.lookup(id); 219 } 220 catch (ComponentException e) 221 { 222 throw new Exception("Unable to lookup client side element role: '" + id + "'", e); 223 } 224 } 225 else 226 { 227 element = _ribbonControlManager.getExtension(id); 228 } 229 230 if (unresolvedItem.isPrimary()) 231 { 232 _primaryMenuItem = element; 233 } 234 235 _menuItems.add(element); 236 _referencedClientSideElement.add(element); 237 } 238 } 239 240 _unresolvedMenuItems = null; 241 } 242 243 class UnresolvedItem 244 { 245 private String _itemId; 246 private boolean _local; 247 private boolean _primary; 248 249 public UnresolvedItem(String id, boolean local) 250 { 251 _itemId = id; 252 _local = local; 253 _primary = false; 254 } 255 256 public UnresolvedItem(String id, boolean local, boolean primary) 257 { 258 _itemId = id; 259 _local = local; 260 _primary = primary; 261 } 262 263 public String getId () 264 { 265 return _itemId; 266 } 267 268 public boolean isLocalItem () 269 { 270 return _local; 271 } 272 273 public boolean isPrimary () 274 { 275 return _primary; 276 } 277 } 278}