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