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