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.Comparator; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.TreeMap; 025import java.util.TreeSet; 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.commons.lang.StringUtils; 033 034import org.ametys.core.right.RightManager.RightResult; 035import org.ametys.core.ui.Callable; 036import org.ametys.core.ui.StaticClientSideElement; 037import org.ametys.core.user.UserIdentity; 038import org.ametys.core.util.I18nUtils; 039import org.ametys.plugins.repository.AmetysObjectResolver; 040import org.ametys.runtime.i18n.I18nizableText; 041import org.ametys.web.repository.page.ModifiablePage; 042import org.ametys.web.repository.page.Page; 043import org.ametys.web.repository.page.ServicesAssignmentHandler; 044import org.ametys.web.service.Service; 045import org.ametys.web.service.ServiceExtensionPoint; 046 047/** 048 * Menu that lists the services 049 */ 050public class ServiceMenu extends AbstractPageMenu 051{ 052 /** The list of content types */ 053 protected ServiceExtensionPoint _serviceExtensionPoint; 054 /** The service assignment handler */ 055 protected ServicesAssignmentHandler _servicesHandler; 056 /** The i18n utils */ 057 protected I18nUtils _i18nUtils; 058 059 private boolean _servicesInitialized; 060 061 @Override 062 public void service(ServiceManager smanager) throws ServiceException 063 { 064 super.service(smanager); 065 066 _serviceExtensionPoint = (ServiceExtensionPoint) smanager.lookup(ServiceExtensionPoint.ROLE); 067 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 068 _servicesHandler = (ServicesAssignmentHandler) smanager.lookup(ServicesAssignmentHandler.ROLE); 069 _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE); 070 } 071 072 @Override 073 protected Script _configureScript(Configuration configuration) throws ConfigurationException 074 { 075 Script script = super._configureScript(configuration); 076 077 for (String serviceId : _serviceExtensionPoint.getExtensionsIds()) 078 { 079 Service service = _serviceExtensionPoint.getExtension(serviceId); 080 081 script.getCSSFiles().addAll(service.getCSSFiles()); 082 script.getCSSFiles().addAll(service.getParametersScript().getCSSFiles()); 083 script.getScriptFiles().addAll(service.getParametersScript().getScriptFiles()); 084 } 085 086 return script; 087 } 088 089 @Override 090 public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters) 091 { 092 if (_serviceExtensionPoint.getExtensionsIds().size() == 0) 093 { 094 // Hide menu if there is no service 095 return new ArrayList<>(); 096 } 097 098 return super.getScripts(ignoreRights, contextParameters); 099 } 100 101 /** 102 * Get the available templates for pages 103 * @param pageId The id of the page 104 * @param zoneName the name of the zone 105 * @return the list of templates' name 106 */ 107 @Callable 108 public Map<String, Object> getAvailableServices (String pageId, String zoneName) 109 { 110 Map<String, Object> result = new HashMap<>(); 111 112 List<String> services = new ArrayList<>(); 113 114 Page page = _resolver.resolveById(pageId); 115 116 if (!(page instanceof ModifiablePage)) 117 { 118 Map<String, Object> pageParams = getPageDefaultParameters(page); 119 pageParams.put("description", getNoModifiablePageDescription(page)); 120 121 result.put("nomodifiable-page", pageParams); 122 } 123 else if (!hasRight(page)) 124 { 125 Map<String, Object> pageParams = getPageDefaultParameters(page); 126 pageParams.put("description", getNoRightPageDescription(page)); 127 128 @SuppressWarnings("unchecked") 129 List<Map<String, Object>> norightPages = (List<Map<String, Object>>) result.get("noright-pages"); 130 norightPages.add(pageParams); 131 } 132 else 133 { 134 Set<String> availablesServicesId = _servicesHandler.getAvailableServices(page, zoneName); 135 for (String serviceId : availablesServicesId) 136 { 137 Service service = _serviceExtensionPoint.getExtension(serviceId); 138 if (hasRight(service, page)) 139 { 140 services.add(serviceId); 141 } 142 143 } 144 } 145 146 result.put("services", services); 147 return result; 148 } 149 150 /** 151 * Determines if user has convenient right on page 152 * @param page The page 153 * @param rightId The right to check 154 * @return true if the user has convenient right 155 */ 156 protected boolean _hasRight (Page page, String rightId) 157 { 158 if (StringUtils.isEmpty(rightId)) 159 { 160 return true; 161 } 162 163 return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, page) == RightResult.RIGHT_ALLOW; 164 } 165 166 @Override 167 protected void _getGalleryItems(Map<String, Object> parameters, Map<String, Object> contextualParameters) 168 { 169 try 170 { 171 _lazyInitializeServiceGallery(); 172 } 173 catch (Exception e) 174 { 175 throw new IllegalStateException("Unable to lookup client side element local components", e); 176 } 177 178 super._getGalleryItems(parameters, contextualParameters); 179 } 180 181 private void _lazyInitializeServiceGallery() throws ConfigurationException 182 { 183 if (!_servicesInitialized) 184 { 185 Map<I18nizableText, Set<Service>> servicesByGroup = _getServicesByGroup(); 186 187 if (servicesByGroup.size() > 0) 188 { 189 GalleryItem galleryItem = new GalleryItem(); 190 191 for (I18nizableText groupLabel : servicesByGroup.keySet()) 192 { 193 GalleryGroup galleryGroup = new GalleryGroup(groupLabel); 194 galleryItem.addGroup(galleryGroup); 195 196 Set<Service> cTypes = servicesByGroup.get(groupLabel); 197 for (Service service : cTypes) 198 { 199 String id = this.getId() + "-" + service.getId(); 200 201 Configuration conf = _getServiceConfiguration (id, service); 202 _galleryItemManager.addComponent(_pluginName, null, id, StaticClientSideElement.class, conf); 203 galleryGroup.addItem(new UnresolvedItem(id, true)); 204 } 205 } 206 207 _galleryItems.add(galleryItem); 208 } 209 210 if (_galleryItems.size() > 0) 211 { 212 try 213 { 214 _galleryItemManager.initialize(); 215 } 216 catch (Exception e) 217 { 218 throw new ConfigurationException("Unable to lookup parameter local components", e); 219 } 220 } 221 } 222 223 _servicesInitialized = true; 224 } 225 226 /** 227 * Get the list of services classified by groups 228 * @return The content types 229 */ 230 protected Map<I18nizableText, Set<Service>> _getServicesByGroup () 231 { 232 Map<I18nizableText, Set<Service>> groups = new TreeMap<>(new I18nizableTextComparator()); 233 234 if (this._script.getParameters().get("services") != null) 235 { 236 String[] serviceIds = ((String) this._script.getParameters().get("services")).split(","); 237 238 for (String serviceId : serviceIds) 239 { 240 Service service = _serviceExtensionPoint.getExtension(serviceId); 241 242 if (isValidService(service)) 243 { 244 addService (service, groups); 245 } 246 } 247 } 248 else 249 { 250 Set<String> serviceIds = _serviceExtensionPoint.getExtensionsIds(); 251 252 for (String serviceId : serviceIds) 253 { 254 Service service = _serviceExtensionPoint.getExtension(serviceId); 255 256 if (isValidService(service)) 257 { 258 addService (service, groups); 259 } 260 } 261 } 262 263 return groups; 264 } 265 266 /** 267 * Add service to groups 268 * @param service The service 269 * @param groups The groups 270 */ 271 protected void addService (Service service, Map<I18nizableText, Set<Service>> groups) 272 { 273 I18nizableText group = service.getCategory(); 274 if ((group.isI18n() && group.getKey().isEmpty()) || (!group.isI18n() && group.getLabel().isEmpty())) 275 { 276 group = new I18nizableText("plugin.web", "PLUGINS_WEB_SERVICE_CATEGORY_90_OTHERS"); 277 } 278 279 if (!groups.containsKey(group)) 280 { 281 groups.put(group, new TreeSet<>(new ServiceComparator())); 282 } 283 284 Set<Service> services = groups.get(group); 285 services.add(service); 286 } 287 288 289 /** 290 * Determines if the service is a valid service for the gallery 291 * @param service The service 292 * @return true if it is a valid service 293 */ 294 protected boolean isValidService (Service service) 295 { 296 return !service.isPrivate(); 297 } 298 299 /** 300 * Get the configuration of the service item 301 * @param id The id of item 302 * @param service The service 303 * @return The configuration 304 */ 305 protected Configuration _getServiceConfiguration (String id, Service service) 306 { 307 DefaultConfiguration conf = new DefaultConfiguration("extension"); 308 conf.setAttribute("id", id); 309 310 DefaultConfiguration classConf = new DefaultConfiguration("class"); 311 classConf.setAttribute("name", "Ametys.ribbon.element.ui.ButtonController"); 312 313 // Label 314 DefaultConfiguration labelConf = new DefaultConfiguration("label"); 315 I18nizableText label = service.getLabel(); 316 if (label.isI18n()) 317 { 318 labelConf.setAttribute("i18n", "true"); 319 labelConf.setValue(label.getCatalogue() + ":" + label.getKey()); 320 } 321 else 322 { 323 labelConf.setValue(label.getLabel()); 324 } 325 classConf.addChild(labelConf); 326 327 // Description 328 DefaultConfiguration descConf = new DefaultConfiguration("description"); 329 I18nizableText description = service.getLabel(); 330 if (description.isI18n()) 331 { 332 descConf.setAttribute("i18n", "true"); 333 descConf.setValue(description.getCatalogue() + ":" + description.getKey()); 334 } 335 else 336 { 337 descConf.setValue(description.getLabel()); 338 } 339 classConf.addChild(descConf); 340 341 // Service id 342 DefaultConfiguration idConf = new DefaultConfiguration("serviceId"); 343 idConf.setValue(service.getId()); 344 classConf.addChild(idConf); 345 346 // Icons or glyph 347 if (service.getIconGlyph() != null) 348 { 349 DefaultConfiguration iconGlyphConf = new DefaultConfiguration("icon-glyph"); 350 iconGlyphConf.setValue(service.getIconGlyph()); 351 classConf.addChild(iconGlyphConf); 352 } 353 if (service.getIconDecorator() != null) 354 { 355 DefaultConfiguration iconDecoratorConf = new DefaultConfiguration("icon-decorator"); 356 iconDecoratorConf.setValue(service.getIconDecorator()); 357 classConf.addChild(iconDecoratorConf); 358 } 359 if (service.getSmallIcon() != null) 360 { 361 DefaultConfiguration iconSmallConf = new DefaultConfiguration("icon-small"); 362 iconSmallConf.setValue(service.getSmallIcon()); 363 classConf.addChild(iconSmallConf); 364 DefaultConfiguration iconMediumConf = new DefaultConfiguration("icon-medium"); 365 iconMediumConf.setValue(service.getMediumIcon()); 366 classConf.addChild(iconMediumConf); 367 DefaultConfiguration iconLargeConf = new DefaultConfiguration("icon-large"); 368 iconLargeConf.setValue(service.getLargeIcon()); 369 classConf.addChild(iconLargeConf); 370 } 371 372 // Parameters action 373 DefaultConfiguration paramsAction = new DefaultConfiguration("params-action"); 374 paramsAction.setValue(service.getParametersScript().getScriptClassname()); 375 classConf.addChild(paramsAction); 376 377 // Common configuration 378 @SuppressWarnings("unchecked") 379 Map<String, Object> commonConfig = (Map<String, Object>) this._script.getParameters().get("items-config"); 380 for (String tagName : commonConfig.keySet()) 381 { 382 DefaultConfiguration c = new DefaultConfiguration(tagName); 383 c.setValue(String.valueOf(commonConfig.get(tagName))); 384 classConf.addChild(c); 385 } 386 387 conf.addChild(classConf); 388 return conf; 389 } 390 391 class ServiceComparator implements Comparator<Service> 392 { 393 @Override 394 public int compare(Service s1, Service s2) 395 { 396 I18nizableText t1 = s1.getLabel(); 397 I18nizableText t2 = s2.getLabel(); 398 399 String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel(); 400 String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel(); 401 402 int compareTo = str1.toString().compareTo(str2.toString()); 403 if (compareTo == 0) 404 { 405 // Content types have same keys but there are not equals, so do not return 0 to add it in TreeSet 406 // Indeed, in a TreeSet implementation two elements that are equal by the method compareTo are, from the standpoint of the set, equal 407 return 1; 408 } 409 return compareTo; 410 } 411 } 412 413 class I18nizableTextComparator implements Comparator<I18nizableText> 414 { 415 @Override 416 public int compare(I18nizableText t1, I18nizableText t2) 417 { 418 String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel(); 419 String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel(); 420 421 return str1.toString().compareTo(str2.toString()); 422 } 423 } 424 425 426 /** 427 * Test if the current user has the right needed by the service to create it. 428 * @param service the service. 429 * @param page the current page. 430 * @return true if the user has the right needed, false otherwise. 431 */ 432 protected boolean hasRight(Service service, Page page) 433 { 434 boolean hasRight = false; 435 436 if (service != null) 437 { 438 String right = service.getRight(); 439 440 if (right == null) 441 { 442 hasRight = true; 443 } 444 else 445 { 446 UserIdentity user = _currentUserProvider.getUser(); 447 hasRight = _rightManager.hasRight(user, right, page) == RightResult.RIGHT_ALLOW; 448 } 449 } 450 451 return hasRight; 452 } 453 454}