001/* 002 * Copyright 2015 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.plugins.linkdirectory.link; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import javax.jcr.RepositoryException; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.logger.AbstractLogEnabled; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.avalon.framework.service.Serviceable; 035import org.apache.cocoon.Constants; 036import org.apache.cocoon.components.ContextHelper; 037import org.apache.cocoon.environment.Context; 038import org.apache.commons.lang3.ArrayUtils; 039import org.apache.commons.lang3.StringUtils; 040 041import org.ametys.cms.FilterNameHelper; 042import org.ametys.core.group.Group; 043import org.ametys.core.group.GroupIdentity; 044import org.ametys.core.group.GroupManager; 045import org.ametys.core.observation.Event; 046import org.ametys.core.observation.ObservationManager; 047import org.ametys.core.ui.Callable; 048import org.ametys.core.upload.Upload; 049import org.ametys.core.upload.UploadManager; 050import org.ametys.core.user.CurrentUserProvider; 051import org.ametys.core.user.User; 052import org.ametys.core.user.UserIdentity; 053import org.ametys.core.user.UserManager; 054import org.ametys.core.util.JSONUtils; 055import org.ametys.plugins.core.user.UserHelper; 056import org.ametys.plugins.explorer.ObservationConstants; 057import org.ametys.plugins.explorer.resources.Resource; 058import org.ametys.plugins.linkdirectory.DirectoryEvents; 059import org.ametys.plugins.linkdirectory.DirectoryHelper; 060import org.ametys.plugins.linkdirectory.Link; 061import org.ametys.plugins.linkdirectory.Link.LinkType; 062import org.ametys.plugins.linkdirectory.dynamic.DynamicInformationProvider; 063import org.ametys.plugins.linkdirectory.dynamic.DynamicInformationProviderExtensionPoint; 064import org.ametys.plugins.linkdirectory.repository.DefaultLink; 065import org.ametys.plugins.linkdirectory.repository.DefaultLinkFactory; 066import org.ametys.plugins.linkdirectory.repository.DefaultTheme; 067import org.ametys.plugins.repository.AmetysObject; 068import org.ametys.plugins.repository.AmetysObjectIterable; 069import org.ametys.plugins.repository.AmetysObjectResolver; 070import org.ametys.plugins.repository.AmetysRepositoryException; 071import org.ametys.plugins.repository.ModifiableAmetysObject; 072import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 073import org.ametys.plugins.repository.TraversableAmetysObject; 074import org.ametys.plugins.repository.UnknownAmetysObjectException; 075import org.ametys.plugins.repository.metadata.BinaryMetadata; 076import org.ametys.web.repository.page.Page; 077import org.ametys.web.repository.site.Site; 078import org.ametys.web.repository.site.SiteManager; 079import org.ametys.web.site.SiteConfigurationExtensionPoint; 080 081/** 082 * DAO for manipulating {@link Link} 083 */ 084public class LinkDAO extends AbstractLogEnabled implements Serviceable, Component, Contextualizable 085{ 086 /** Avalon Role */ 087 public static final String ROLE = LinkDAO.class.getName(); 088 089 private static UserManager _userManager; 090 private static GroupManager _groupManager; 091 private UserHelper _userHelper; 092 093 private AmetysObjectResolver _resolver; 094 095 private ObservationManager _observationManager; 096 private SiteManager _siteManager; 097 private CurrentUserProvider _currentUserProvider; 098 private UploadManager _uploadManager; 099 private SiteConfigurationExtensionPoint _siteConfEP; 100 101 private JSONUtils _jsonUtils; 102 private DirectoryHelper _directoryHelper; 103 104 private org.apache.avalon.framework.context.Context _context; 105 private Context _cocoonContext; 106 107 private DynamicInformationProviderExtensionPoint _dynamicInfoExtensionPoint; 108 109 @Override 110 public void service(ServiceManager manager) throws ServiceException 111 { 112 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 113 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 114 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 115 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 116 _uploadManager = (UploadManager) manager.lookup(UploadManager.ROLE); 117 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 118 _groupManager = (GroupManager) manager.lookup(GroupManager.ROLE); 119 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 120 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 121 _siteConfEP = (SiteConfigurationExtensionPoint) manager.lookup(SiteConfigurationExtensionPoint.ROLE); 122 _directoryHelper = (DirectoryHelper) manager.lookup(DirectoryHelper.ROLE); 123 _dynamicInfoExtensionPoint = (DynamicInformationProviderExtensionPoint) manager.lookup(DynamicInformationProviderExtensionPoint.ROLE); 124 } 125 126 @Override 127 public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException 128 { 129 _context = context; 130 _cocoonContext = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 131 } 132 133 /** 134 * Create a new link 135 * @param parameters a map of the following parameters for the link : siteName, language, url, title, content, url-alternative, picture, picture#type, picture-alternative, themes, grant-any-user, fousers, fogroups 136 * @return The new link id 137 */ 138 @Callable 139 public Map<String, Object> createLink(Map<String, Object> parameters) 140 { 141 Map<String, Object> result = new HashMap<>(); 142 143 String siteName = (String) parameters.get("siteName"); 144 String language = (String) parameters.get("lang"); 145 Site site = _siteManager.getSite(siteName); 146 147 String url = StringUtils.defaultString((String) parameters.get("url")); 148 String internalUrl = StringUtils.defaultString((String) parameters.get("internal-url")); 149 150 // Check that the word doesn't already exist. 151 if (_urlExists(url, internalUrl, siteName, language)) 152 { 153 result.put("already-exists", true); 154 return result; 155 } 156 157 ModifiableTraversableAmetysObject rootNode = _directoryHelper.getLinksNode(site, language); 158 159 String name = url; 160 if (StringUtils.isBlank(name)) 161 { 162 name = internalUrl; 163 } 164 DefaultLink link = _createLink(name, rootNode); 165 _setValues(link, parameters); 166 167 rootNode.saveChanges(); 168 169 // Notify listeners 170 Map<String, Object> eventParams = new HashMap<>(); 171 eventParams.put(ObservationConstants.ARGS_ID, link.getId()); 172 eventParams.put(ObservationConstants.ARGS_PARENT_ID, rootNode.getId()); 173 eventParams.put(ObservationConstants.ARGS_NAME, link.getName()); 174 eventParams.put(ObservationConstants.ARGS_PATH, link.getPath()); 175 176 _observationManager.notify(new Event(DirectoryEvents.LINK_CREATED, _currentUserProvider.getUser(), eventParams)); 177 178 return convertLink2JsonObject(link); 179 } 180 181 /** 182 * Create a new user link 183 * @param parameters a map of the following parameters for the link : siteName, language, url, title, content, url-alternative, picture, picture#type, picture-alternative 184 * @return The new link id 185 */ 186 public Map<String, Object> createUserLink(Map<String, Object> parameters) 187 { 188 Map<String, Object> result = new HashMap<>(); 189 190 UserIdentity currentUser = _currentUserProvider.getUser(); 191 if (currentUser == null) 192 { 193 result.put("unauthenticated-user", true); 194 return result; 195 } 196 197 String siteName = (String) parameters.get("siteName"); 198 String language = (String) parameters.get("lang"); 199 Site site = _siteManager.getSite(siteName); 200 201 String url = StringUtils.defaultString((String) parameters.get("url")); 202 String internalUrl = StringUtils.defaultString((String) parameters.get("internal-url")); 203 204 // Check that the word doesn't already exist for the given user. 205 if (_urlExistsForUser(url, internalUrl, siteName, language, currentUser)) 206 { 207 result.put("already-exists", true); 208 return result; 209 } 210 211 ModifiableTraversableAmetysObject rootNodeForUser = _directoryHelper.getLinksForUserNode(site, language, currentUser); 212 213 String name = url; 214 if (StringUtils.isBlank(name)) 215 { 216 name = internalUrl; 217 } 218 DefaultLink link = _createLink(name, rootNodeForUser); 219 _setLinkValues(link, parameters); 220 221 rootNodeForUser.saveChanges(); 222 223 // Notify listeners 224 Map<String, Object> eventParams = new HashMap<>(); 225 eventParams.put(ObservationConstants.ARGS_ID, link.getId()); 226 eventParams.put(ObservationConstants.ARGS_PARENT_ID, rootNodeForUser.getId()); 227 eventParams.put(ObservationConstants.ARGS_NAME, link.getName()); 228 eventParams.put(ObservationConstants.ARGS_PATH, link.getPath()); 229 230 _observationManager.notify(new Event(DirectoryEvents.LINK_CREATED, _currentUserProvider.getUser(), eventParams)); 231 232 return convertLink2JsonObject(link); 233 } 234 235 /** 236 * Updates a link 237 * @param parameters a map of the following parameters for the link : siteName, language, id, url, title, content, url-alternative, picture, picture#type, picture-alternative, themes, grant-any-user, fousers, fogroups 238 * @return the update link 239 */ 240 @Callable 241 public Map<String, Object> updateLink(Map<String, Object> parameters) 242 { 243 Map<String, Object> result = new HashMap<>(); 244 245 String siteName = (String) parameters.get("siteName"); 246 String language = (String) parameters.get("language"); 247 String id = StringUtils.defaultString((String) parameters.get("id")); 248 String url = StringUtils.defaultString((String) parameters.get("url")); 249 String internalUrl = StringUtils.defaultString((String) parameters.get("internal-url")); 250 251 try 252 { 253 DefaultLink link = _resolver.resolveById(id); 254 255 // If the url was changed, check that the new url doesn't already exist. 256 if (!link.getUrl().equals(url) && _urlExists(url, internalUrl, siteName, language)) 257 { 258 result.put("already-exists", true); 259 return result; 260 } 261 262 _setValues(link, parameters); 263 link.saveChanges(); 264 265 // Notify listeners 266 Map<String, Object> eventParams = new HashMap<>(); 267 eventParams.put(ObservationConstants.ARGS_ID, link.getId()); 268 eventParams.put(ObservationConstants.ARGS_NAME, link.getName()); 269 eventParams.put(ObservationConstants.ARGS_PATH, link.getPath()); 270 271 _observationManager.notify(new Event(DirectoryEvents.LINK_MODIFIED, _currentUserProvider.getUser(), eventParams)); 272 273 return convertLink2JsonObject(link); 274 } 275 catch (UnknownAmetysObjectException e) 276 { 277 result.put("unknown-link", true); 278 return result; 279 } 280 catch (AmetysRepositoryException e) 281 { 282 throw new IllegalStateException(e); 283 } 284 } 285 286 private void _setValues(Link link, Map<String, Object> values) 287 { 288 // Set values (url, type, title, etc.) 289 _setLinkValues(link, values); 290 291 // Set themes 292 @SuppressWarnings("unchecked") 293 List<String> themes = (List<String>) values.get("themes"); 294 _setThemes(link, themes); 295 296 // Set restrictions 297 boolean grantAnyUser = values.containsKey("grant-any-user") ? (Boolean) values.get("grant-any-user") : false; 298 String grantedUsersAsString = StringUtils.defaultString((String) values.get("fousers")); 299 String grantedGroupsAsString = StringUtils.defaultString((String) values.get("fogroups")); 300 _setRestrictions(link, grantAnyUser, grantedUsersAsString, grantedGroupsAsString); 301 } 302 303 private void _setLinkValues(Link link, Map<String, Object> values) 304 { 305 String url = StringUtils.defaultString((String) values.get("url")); 306 String dynInfoProviderId = StringUtils.defaultString((String) values.get("dynamic-info-provider")); 307 String internalUrl = StringUtils.defaultString((String) values.get("internal-url")); 308 String urlType = StringUtils.defaultString((String) values.get("url-type")); 309 String title = StringUtils.defaultString((String) values.get("title")); 310 String content = StringUtils.defaultString((String) values.get("content")); 311 String alternative = StringUtils.defaultString((String) values.get("url-alternative")); 312 313 String pictureAsStr = StringUtils.defaultString((String) values.get("picture")); 314 String pictureAlternative = StringUtils.defaultString((String) values.get("picture-alternative")); 315 316 link.setUrl(LinkType.valueOf(urlType), url); 317 link.setDynamicInformationProvider(dynInfoProviderId); 318 link.setInternalUrl(internalUrl); 319 link.setTitle(title); 320 link.setContent(content); 321 link.setAlternative(alternative); 322 link.setPictureAlternative(pictureAlternative); 323 324 _setPicture(link, pictureAsStr); 325 } 326 327 private void _setThemes(Link link, List<String> themes) 328 { 329 link.setThemes(themes.toArray(new String[themes.size()])); 330 } 331 332 private void _setPicture(Link link, String valueAsStr) 333 { 334 if (StringUtils.isNotEmpty(valueAsStr)) 335 { 336 Map<String, Object> picture = _jsonUtils.convertJsonToMap(valueAsStr); 337 338 if (!picture.isEmpty()) 339 { 340 String pictureType = (String) picture.get("type"); 341 String value = (String) picture.get("id"); 342 343 if (pictureType.equals("explorer") && !"untouched".equals(value)) 344 { 345 link.setResourcePicture(value); 346 } 347 else if (!"untouched".equals(value)) 348 { 349 UserIdentity user = _currentUserProvider.getUser(); 350 Upload upload = _uploadManager.getUpload(user, value); 351 352 String filename = upload.getFilename(); 353 String mimeType = upload.getMimeType() != null ? upload.getMimeType() : _cocoonContext.getMimeType(filename); 354 String finalMimeType = mimeType != null ? mimeType : "application/unknown"; 355 356 link.setExternalPicture(finalMimeType, filename, upload.getInputStream()); 357 } 358 } 359 else 360 { 361 // Remove picture 362 link.setNoPicture(); 363 } 364 365 } 366 else 367 { 368 // Remove picture 369 link.setNoPicture(); 370 } 371 } 372 373 private void _setRestrictions(Link link, boolean grantAnyUser, String grantedUsersAsString, String grantedGroupsAsString) 374 { 375 Set<UserIdentity> grantedUsers = new HashSet<>(); 376 List<Object> grantedUsersAsList = _jsonUtils.convertJsonToList(grantedUsersAsString); 377 for (Object obj : grantedUsersAsList) 378 { 379 @SuppressWarnings("unchecked") 380 Map<String, Object> grantedUser = (Map<String, Object>) obj; 381 String login = (String) grantedUser.get("login"); 382 String populationId = (String) grantedUser.get("populationId"); 383 384 if (login != null && populationId != null) 385 { 386 grantedUsers.add(new UserIdentity(login, populationId)); 387 } 388 } 389 390 Set<GroupIdentity> grantedGroups = new HashSet<>(); 391 List<Object> grantedGroupsAsList = _jsonUtils.convertJsonToList(grantedGroupsAsString); 392 for (Object obj : grantedGroupsAsList) 393 { 394 @SuppressWarnings("unchecked") 395 Map<String, Object> grantedGroup = (Map<String, Object>) obj; 396 String groupId = (String) grantedGroup.get("groupId"); 397 String groupDirectory = (String) grantedGroup.get("groupDirectory"); 398 399 if (groupId != null && groupDirectory != null) 400 { 401 grantedGroups.add(new GroupIdentity(groupId, groupDirectory)); 402 } 403 } 404 405 link.setGrantAnyUser(grantAnyUser); 406 link.setGrantedUsers(grantedUsers.toArray(new UserIdentity[grantedUsers.size()])); 407 link.setGrantedGroups(grantedGroups.toArray(new GroupIdentity[grantedGroups.size()])); 408 } 409 410 /** 411 * Delete one or multiples links 412 * @param ids a list of links' ids 413 * @return true if all the links were deleted, false if at least one link could not be delete. 414 */ 415 @Callable 416 public List<String> deleteLinks(List<String> ids) 417 { 418 List<String> result = new ArrayList<>(); 419 420 for (String id : ids) 421 { 422 try 423 { 424 DefaultLink link = _resolver.resolveById(id); 425 426 String siteName = link.getSiteName(); 427 String language = link.getLanguage(); 428 String linkId = link.getId(); 429 String name = link.getName(); 430 String path = link.getPath(); 431 432 ModifiableAmetysObject parent = link.getParent(); 433 link.remove(); 434 435 parent.saveChanges(); 436 437 // Notify listeners 438 Map<String, Object> eventParams = new HashMap<>(); 439 eventParams.put(ObservationConstants.ARGS_ID, linkId); 440 eventParams.put(ObservationConstants.ARGS_NAME, name); 441 eventParams.put(ObservationConstants.ARGS_PATH, path); 442 eventParams.put("siteName", siteName); 443 eventParams.put("language", language); 444 _observationManager.notify(new Event(DirectoryEvents.LINK_DELETED, _currentUserProvider.getUser(), eventParams)); 445 446 result.add(linkId); 447 } 448 catch (UnknownAmetysObjectException e) 449 { 450 // ignore the id and continue the deletion. 451 getLogger().error("Unable to delete the link of id '" + id + ", because it does not exist.", e); 452 } 453 } 454 455 return result; 456 } 457 458 /** 459 * Move a link in the list 460 * @param id the link id 461 * @param role the move action 462 * @throws RepositoryException if a repository error occurs. 463 * @return the moved link in JSON 464 */ 465 @Callable 466 public Map<String, Object> moveLink(String id, String role) throws RepositoryException 467 { 468 DefaultLink link = _resolver.resolveById(id); 469 470 switch (role) 471 { 472 case "move-first": 473 _moveFirst(link); 474 break; 475 case "move-up": 476 _moveUp(link); 477 break; 478 case "move-down": 479 _moveDown(link); 480 break; 481 case "move-last": 482 _moveLast(link); 483 break; 484 default: 485 break; 486 } 487 488 return convertLink2JsonObject(link); 489 } 490 491 492 493 /** 494 * Get the JSON object representing a link 495 * @param id the id of link 496 * @return the link as JSON object 497 */ 498 @Callable 499 public Map<String, Object> getLink (String id) 500 { 501 DefaultLink link = _resolver.resolveById(id); 502 return convertLink2JsonObject(link); 503 } 504 505 /** 506 * Determines if the restriction IP parameter is not empty 507 * @param siteName the site name 508 * @return true if the restriction IP parameter is not empty 509 */ 510 @Callable 511 public boolean isInternalURLAllowed (String siteName) 512 { 513 String allowedIpParameter = _siteConfEP.getValueAsString(siteName, "allowed-ip"); 514 return StringUtils.isNotBlank(allowedIpParameter); 515 } 516 517 /** 518 * Returns the list of providers of dynamic information as json object 519 * @return the providers of dynamic information 520 */ 521 @Callable 522 public List<Map<String, Object>> getDynamicInformationProviders() 523 { 524 List<Map<String, Object>> result = new ArrayList<>(); 525 526 for (String id : _dynamicInfoExtensionPoint.getExtensionsIds()) 527 { 528 DynamicInformationProvider provider = _dynamicInfoExtensionPoint.getExtension(id); 529 Map<String, Object> info = new HashMap<>(); 530 info.put("id", provider.getId()); 531 info.put("label", provider.getLabel()); 532 result.add(info); 533 } 534 535 return result; 536 } 537 538 /** 539 * Convert a link to JSON object 540 * @param link the link 541 * @return the link as JSON object 542 */ 543 public Map<String, Object> convertLink2JsonObject (DefaultLink link) 544 { 545 Map<String, Object> infos = new HashMap<>(); 546 547 infos.put("id", link.getId()); 548 infos.put("lang", link.getLanguage()); 549 infos.put("url", link.getUrl()); 550 infos.put("dynamicInfoProvider", link.getDynamicInformationProvider()); 551 infos.put("internalUrl", link.getInternalUrl()); 552 infos.put("urlType", link.getUrlType().toString()); 553 554 if (link.getUrlType() == LinkType.PAGE) 555 { 556 String pageId = link.getUrl(); 557 try 558 { 559 Page page = _resolver.resolveById(pageId); 560 infos.put("pageTitle", page.getTitle()); 561 } 562 catch (UnknownAmetysObjectException e) 563 { 564 infos.put("unknownPage", true); 565 } 566 } 567 568 infos.put("title", link.getTitle()); 569 infos.put("alternative", link.getAlternative()); 570 571 infos.put("content", StringUtils.defaultString(link.getContent())); 572 infos.put("pictureAlternative", StringUtils.defaultString(link.getPictureAlternative())); 573 574 Map<String, Object> pictureInfos = new HashMap<>(); 575 String pictureType = link.getPictureType(); 576 577 TraversableAmetysObject parent = link.getParent(); 578 infos.put("position", parent.getChildPosition(link)); 579 infos.put("count", parent.getChildren().getSize()); 580 581 if (pictureType.equals("resource")) 582 { 583 String resourceId = link.getResourcePictureId(); 584 pictureInfos.put("id", resourceId); 585 try 586 { 587 Resource resource = _resolver.resolveById(resourceId); 588 589 pictureInfos.put("filename", resource.getName()); 590 pictureInfos.put("size", resource.getLength()); 591 pictureInfos.put("type", "explorer"); 592 pictureInfos.put("lastModified", resource.getLastModified()); 593 594 String contextPath = ContextHelper.getRequest(_context).getContextPath(); 595 String viewUrl = contextPath + "/plugins/explorer/resource?id=" + resource.getId(); 596 String downloadUrl = viewUrl + "&download=true"; 597 pictureInfos.put("viewUrl", viewUrl); 598 pictureInfos.put("downloadUrl", downloadUrl); 599 } 600 catch (UnknownAmetysObjectException e) 601 { 602 getLogger().error("The resource of id'" + resourceId + "' does not exist anymore. The picture for link of id '" + link.getId() + "' will be ignored.", e); 603 infos.put("pictureNotFound", true); 604 } 605 } 606 else if (pictureType.equals("external")) 607 { 608 BinaryMetadata picMeta = link.getExternalPicture(); 609 610 pictureInfos.put("path", DefaultLink.PROPERTY_PICTURE); 611 pictureInfos.put("filename", picMeta.getFilename()); 612 pictureInfos.put("size", picMeta.getLength()); 613 pictureInfos.put("lastModified", picMeta.getLastModified()); 614 pictureInfos.put("type", "metadata"); 615 616 String contextPath = ContextHelper.getRequest(_context).getContextPath(); 617 String viewUrl = contextPath + "/plugins/cms/binaryMetadata/" + DefaultLink.PROPERTY_PICTURE + "?objectId=" + link.getId(); 618 String downloadUrl = viewUrl + "&download=true"; 619 pictureInfos.put("viewUrl", viewUrl); 620 pictureInfos.put("downloadUrl", downloadUrl); 621 622 } 623 infos.put("picture", pictureInfos); 624 625 infos.put("grantAnyUser", String.valueOf(link.isAllowedAnyUser())); 626 627 boolean isAccessLimited = link.isAllowedAnyUser() || ArrayUtils.isNotEmpty(link.getGrantedUsers()) || ArrayUtils.isNotEmpty(link.getGrantedGroups()); 628 infos.put("isRestricted", isAccessLimited); 629 630 // Themes 631 List<Map<String, String>> themesList = new ArrayList<>(); 632 for (String themeId : link.getThemes()) 633 { 634 try 635 { 636 DefaultTheme theme = _resolver.resolveById(themeId); 637 Map<String, String> themeData = new HashMap<>(); 638 themeData.put("id", themeId); 639 themeData.put("label", theme.getLabel()); 640 themesList.add(themeData); 641 } 642 catch (UnknownAmetysObjectException e) 643 { 644 // Theme does not exist anymore 645 } 646 } 647 648 infos.put("themes", themesList); 649 650 List<Map<String, Object>> grantedUserList = new ArrayList<>(); 651 for (UserIdentity grantedUser : link.getGrantedUsers()) 652 { 653 User user = _userManager.getUser(grantedUser); 654 if (user != null) 655 { 656 grantedUserList.add(_userHelper.user2json(user)); 657 } 658 } 659 infos.put("grantedUsers", grantedUserList); 660 661 List<Map<String, String>> grantedGroupList = new ArrayList<>(); 662 for (GroupIdentity grantedGroup : link.getGrantedGroups()) 663 { 664 Group group = _groupManager.getGroup(grantedGroup); 665 if (group != null) 666 { 667 Map<String, String> groupData = new HashMap<>(); 668 groupData.put("groupId", grantedGroup.getId()); 669 groupData.put("groupDirectory", grantedGroup.getDirectoryId()); 670 groupData.put("label", group.getLabel()); 671 grantedGroupList.add(groupData); 672 } 673 } 674 infos.put("grantedGroups", grantedGroupList); 675 676 return infos; 677 } 678 679 /** 680 * Get links infos 681 * @param linkIds the link id 682 * @return the link infos 683 */ 684 @Callable 685 public List<Map<String, Object>> getLinks(List<String> linkIds) 686 { 687 List<Map<String, Object>> result = new ArrayList<>(); 688 689 for (String linkId: linkIds) 690 { 691 try 692 { 693 result.add(getLink(linkId)); 694 } 695 catch (UnknownAmetysObjectException e) 696 { 697 // does not exists 698 } 699 } 700 return result; 701 } 702 703 /** 704 * Test if a link with the specified url or internal url exists in the directory. 705 * @param url the url to test. 706 * @param internalUrl the internal url to test 707 * @param siteName the site name. 708 * @param language the language. 709 * @return true if the link exists. 710 * @throws AmetysRepositoryException if a repository error occurs. 711 */ 712 protected boolean _urlExists(String url, String internalUrl, String siteName, String language) throws AmetysRepositoryException 713 { 714 boolean externalLinkExists = false; 715 if (StringUtils.isNotBlank(url)) 716 { 717 String externalLinkXpathQuery = _directoryHelper.getUrlExistsQuery(siteName, language, url); 718 try (AmetysObjectIterable<DefaultLink> externalLinks = _resolver.query(externalLinkXpathQuery);) 719 { 720 externalLinkExists = externalLinks.iterator().hasNext(); 721 } 722 } 723 724 boolean internalLinkExists = false; 725 if (StringUtils.isNotBlank(internalUrl)) 726 { 727 String internalLinkXpathQuery = _directoryHelper.getUrlExistsQuery(siteName, language, internalUrl); 728 try (AmetysObjectIterable<DefaultLink> internalLinks = _resolver.query(internalLinkXpathQuery);) 729 { 730 internalLinkExists = internalLinks.iterator().hasNext(); 731 } 732 } 733 734 return externalLinkExists || internalLinkExists; 735 } 736 737 /** 738 * Test if a link with the specified url or internal url exists in the directory for the given user. 739 * @param url the url to test. 740 * @param internalUrl the internal url to test 741 * @param siteName the site name. 742 * @param language the language. 743 * @param user The user identity 744 * @return true if the link exists for the given user. 745 * @throws AmetysRepositoryException if a repository error occurs. 746 */ 747 protected boolean _urlExistsForUser(String url, String internalUrl, String siteName, String language, UserIdentity user) throws AmetysRepositoryException 748 { 749 boolean externalLinkExists = false; 750 if (StringUtils.isNotBlank(url)) 751 { 752 String externalLinkXpathQuery = _directoryHelper.getUrlExistsForUserQuery(siteName, language, url, user); 753 try (AmetysObjectIterable<DefaultLink> externalLinks = _resolver.query(externalLinkXpathQuery);) 754 { 755 externalLinkExists = externalLinks.iterator().hasNext(); 756 } 757 } 758 759 boolean internalLinkExists = false; 760 if (StringUtils.isNotBlank(internalUrl)) 761 { 762 String internalLinkXpathQuery = _directoryHelper.getUrlExistsForUserQuery(siteName, language, internalUrl, user); 763 try (AmetysObjectIterable<DefaultLink> internalLinks = _resolver.query(internalLinkXpathQuery);) 764 { 765 internalLinkExists = internalLinks.iterator().hasNext(); 766 } 767 } 768 769 return externalLinkExists || internalLinkExists; 770 } 771 772 /** 773 * Create the link object. 774 * @param name the desired link name. 775 * @param rootNode the links root node. 776 * @return the created Link. 777 */ 778 protected DefaultLink _createLink(String name, ModifiableTraversableAmetysObject rootNode) 779 { 780 String originalName = FilterNameHelper.filterName(name); 781 782 // Find unique name 783 String uniqueName = originalName; 784 int index = 2; 785 while (rootNode.hasChild(uniqueName)) 786 { 787 uniqueName = originalName + "-" + (index++); 788 } 789 790 return rootNode.createChild(uniqueName, DefaultLinkFactory.LINK_NODE_TYPE); 791 } 792 793 /** 794 * Move link the first position 795 * @param link the link to move 796 * @throws RepositoryException if an error occurs while exploring the repository 797 */ 798 private void _moveFirst(DefaultLink link) throws RepositoryException 799 { 800 try (AmetysObjectIterable<AmetysObject> children = ((TraversableAmetysObject) link.getParent()).getChildren();) 801 { 802 // Resolve the link in the same session or the linkRoot.saveChanges() call below won't see the order changes. 803 link.orderBefore(((TraversableAmetysObject) link.getParent()).getChildren().iterator().next()); 804 ((ModifiableAmetysObject) link.getParent()).saveChanges(); 805 } 806 } 807 808 /** 809 * Move link after its following 810 * @param link the link to move down 811 * @throws RepositoryException if an error occurs while exploring the repository 812 */ 813 private void _moveDown(DefaultLink link) throws RepositoryException 814 { 815 TraversableAmetysObject parentNode = link.getParent(); 816 AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren(); 817 Iterator<AmetysObject> it = siblings.iterator(); 818 819 boolean iterate = true; 820 while (it.hasNext() && iterate) 821 { 822 DefaultLink sibling = (DefaultLink) it.next(); 823 iterate = !sibling.getName().equals(link.getName()); 824 } 825 826 if (it.hasNext()) 827 { 828 // Move the link after his next sibling: move the next sibling before the link to move. 829 DefaultLink nextLink = (DefaultLink) it.next(); 830 nextLink.orderBefore(link); 831 832 link.saveChanges(); 833 } 834 } 835 836 /** 837 * Move link before its preceding 838 * @param link the link to move up 839 * @throws RepositoryException if an error occurs while exploring the repository 840 */ 841 private void _moveUp(DefaultLink link) throws RepositoryException 842 { 843 TraversableAmetysObject parentNode = link.getParent(); 844 AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren(); 845 Iterator<AmetysObject> it = siblings.iterator(); 846 DefaultLink previousLink = null; 847 848 while (it.hasNext()) 849 { 850 DefaultLink sibling = (DefaultLink) it.next(); 851 if (sibling.getName().equals(link.getName())) 852 { 853 break; 854 } 855 856 previousLink = sibling; 857 } 858 859 if (previousLink != null) 860 { 861 // Move the link after his next sibling: move the next sibling before the link to move. 862 link.orderBefore(previousLink); 863 link.saveChanges(); 864 } 865 } 866 867 /** 868 * Move link to the last position. 869 * @param link the link to move up 870 * @throws RepositoryException if an error occurs while exploring the repository 871 */ 872 private void _moveLast(DefaultLink link) throws RepositoryException 873 { 874 link.moveTo(link.getParent(), false); 875 ((ModifiableAmetysObject) link.getParent()).saveChanges(); 876 } 877}