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