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 // Check the dynamic provider still exists 317 if (!_dynamicInfoExtensionPoint.hasExtension(dynInfoProviderId)) 318 { 319 dynInfoProviderId = ""; 320 } 321 322 link.setUrl(LinkType.valueOf(urlType), url); 323 link.setDynamicInformationProvider(dynInfoProviderId); 324 link.setInternalUrl(internalUrl); 325 link.setTitle(title); 326 link.setContent(content); 327 link.setAlternative(alternative); 328 link.setPictureAlternative(pictureAlternative); 329 330 _setPicture(link, pictureAsStr); 331 } 332 333 private void _setThemes(Link link, List<String> themes) 334 { 335 link.setThemes(themes.toArray(new String[themes.size()])); 336 } 337 338 private void _setPicture(Link link, String valueAsStr) 339 { 340 if (StringUtils.isNotEmpty(valueAsStr)) 341 { 342 Map<String, Object> picture = _jsonUtils.convertJsonToMap(valueAsStr); 343 344 if (!picture.isEmpty()) 345 { 346 String pictureType = (String) picture.get("type"); 347 String value = (String) picture.get("id"); 348 349 if (pictureType.equals("explorer") && !"untouched".equals(value)) 350 { 351 link.setResourcePicture(value); 352 } 353 else if (pictureType.equals("glyph")) 354 { 355 link.setPictureGlyph(value); 356 } 357 else if (!"untouched".equals(value)) 358 { 359 UserIdentity user = _currentUserProvider.getUser(); 360 Upload upload = _uploadManager.getUpload(user, value); 361 362 String filename = upload.getFilename(); 363 String mimeType = upload.getMimeType() != null ? upload.getMimeType() : _cocoonContext.getMimeType(filename); 364 String finalMimeType = mimeType != null ? mimeType : "application/unknown"; 365 366 link.setExternalPicture(finalMimeType, filename, upload.getInputStream()); 367 } 368 } 369 else 370 { 371 // Remove picture 372 link.setNoPicture(); 373 } 374 375 } 376 else 377 { 378 // Remove picture 379 link.setNoPicture(); 380 } 381 } 382 383 private void _setRestrictions(Link link, boolean grantAnyUser, String grantedUsersAsString, String grantedGroupsAsString) 384 { 385 Set<UserIdentity> grantedUsers = new HashSet<>(); 386 List<Object> grantedUsersAsList = _jsonUtils.convertJsonToList(grantedUsersAsString); 387 for (Object obj : grantedUsersAsList) 388 { 389 @SuppressWarnings("unchecked") 390 Map<String, Object> grantedUser = (Map<String, Object>) obj; 391 String login = (String) grantedUser.get("login"); 392 String populationId = (String) grantedUser.get("populationId"); 393 394 if (login != null && populationId != null) 395 { 396 grantedUsers.add(new UserIdentity(login, populationId)); 397 } 398 } 399 400 Set<GroupIdentity> grantedGroups = new HashSet<>(); 401 List<Object> grantedGroupsAsList = _jsonUtils.convertJsonToList(grantedGroupsAsString); 402 for (Object obj : grantedGroupsAsList) 403 { 404 @SuppressWarnings("unchecked") 405 Map<String, Object> grantedGroup = (Map<String, Object>) obj; 406 String groupId = (String) grantedGroup.get("groupId"); 407 String groupDirectory = (String) grantedGroup.get("groupDirectory"); 408 409 if (groupId != null && groupDirectory != null) 410 { 411 grantedGroups.add(new GroupIdentity(groupId, groupDirectory)); 412 } 413 } 414 415 link.setGrantAnyUser(grantAnyUser); 416 link.setGrantedUsers(grantedUsers.toArray(new UserIdentity[grantedUsers.size()])); 417 link.setGrantedGroups(grantedGroups.toArray(new GroupIdentity[grantedGroups.size()])); 418 } 419 420 /** 421 * Delete one or multiples links 422 * @param ids a list of links' ids 423 * @return true if all the links were deleted, false if at least one link could not be delete. 424 */ 425 @Callable 426 public List<String> deleteLinks(List<String> ids) 427 { 428 List<String> result = new ArrayList<>(); 429 430 for (String id : ids) 431 { 432 try 433 { 434 DefaultLink link = _resolver.resolveById(id); 435 436 String siteName = link.getSiteName(); 437 String language = link.getLanguage(); 438 String linkId = link.getId(); 439 String name = link.getName(); 440 String path = link.getPath(); 441 442 ModifiableAmetysObject parent = link.getParent(); 443 link.remove(); 444 445 parent.saveChanges(); 446 447 // Notify listeners 448 Map<String, Object> eventParams = new HashMap<>(); 449 eventParams.put(ObservationConstants.ARGS_ID, linkId); 450 eventParams.put(ObservationConstants.ARGS_NAME, name); 451 eventParams.put(ObservationConstants.ARGS_PATH, path); 452 eventParams.put("siteName", siteName); 453 eventParams.put("language", language); 454 _observationManager.notify(new Event(DirectoryEvents.LINK_DELETED, _currentUserProvider.getUser(), eventParams)); 455 456 result.add(linkId); 457 } 458 catch (UnknownAmetysObjectException e) 459 { 460 // ignore the id and continue the deletion. 461 getLogger().error("Unable to delete the link of id '" + id + ", because it does not exist.", e); 462 } 463 } 464 465 return result; 466 } 467 468 /** 469 * Move a link in the list 470 * @param id the link id 471 * @param role the move action 472 * @throws RepositoryException if a repository error occurs. 473 * @return the moved link in JSON 474 */ 475 @Callable 476 public Map<String, Object> moveLink(String id, String role) throws RepositoryException 477 { 478 DefaultLink link = _resolver.resolveById(id); 479 480 switch (role) 481 { 482 case "move-first": 483 _moveFirst(link); 484 break; 485 case "move-up": 486 _moveUp(link); 487 break; 488 case "move-down": 489 _moveDown(link); 490 break; 491 case "move-last": 492 _moveLast(link); 493 break; 494 default: 495 break; 496 } 497 498 return convertLink2JsonObject(link); 499 } 500 501 502 503 /** 504 * Get the JSON object representing a link 505 * @param id the id of link 506 * @return the link as JSON object 507 */ 508 @Callable 509 public Map<String, Object> getLink (String id) 510 { 511 DefaultLink link = _resolver.resolveById(id); 512 return convertLink2JsonObject(link); 513 } 514 515 /** 516 * Determines if the restriction IP parameter is not empty 517 * @param siteName the site name 518 * @return true if the restriction IP parameter is not empty 519 */ 520 @Callable 521 public boolean isInternalURLAllowed (String siteName) 522 { 523 String allowedIpParameter = _siteConfEP.getValueAsString(siteName, "allowed-ip"); 524 return StringUtils.isNotBlank(allowedIpParameter); 525 } 526 527 /** 528 * Returns the list of providers of dynamic information as json object 529 * @return the providers of dynamic information 530 */ 531 @Callable 532 public List<Map<String, Object>> getDynamicInformationProviders() 533 { 534 List<Map<String, Object>> result = new ArrayList<>(); 535 536 for (String id : _dynamicInfoExtensionPoint.getExtensionsIds()) 537 { 538 DynamicInformationProvider provider = _dynamicInfoExtensionPoint.getExtension(id); 539 Map<String, Object> info = new HashMap<>(); 540 info.put("id", provider.getId()); 541 info.put("label", provider.getLabel()); 542 result.add(info); 543 } 544 545 return result; 546 } 547 548 /** 549 * Convert a link to JSON object 550 * @param link the link 551 * @return the link as JSON object 552 */ 553 public Map<String, Object> convertLink2JsonObject (DefaultLink link) 554 { 555 Map<String, Object> infos = new HashMap<>(); 556 557 infos.put("id", link.getId()); 558 infos.put("lang", link.getLanguage()); 559 infos.put("url", link.getUrl()); 560 infos.put("dynamicInfoProvider", link.getDynamicInformationProvider()); 561 infos.put("internalUrl", link.getInternalUrl()); 562 infos.put("urlType", link.getUrlType().toString()); 563 564 if (link.getUrlType() == LinkType.PAGE) 565 { 566 String pageId = link.getUrl(); 567 try 568 { 569 Page page = _resolver.resolveById(pageId); 570 infos.put("pageTitle", page.getTitle()); 571 } 572 catch (UnknownAmetysObjectException e) 573 { 574 infos.put("unknownPage", true); 575 } 576 } 577 578 infos.put("title", link.getTitle()); 579 infos.put("alternative", link.getAlternative()); 580 581 infos.put("content", StringUtils.defaultString(link.getContent())); 582 infos.put("pictureAlternative", StringUtils.defaultString(link.getPictureAlternative())); 583 584 Map<String, Object> pictureInfos = new HashMap<>(); 585 String pictureType = link.getPictureType(); 586 587 TraversableAmetysObject parent = link.getParent(); 588 infos.put("position", parent.getChildPosition(link)); 589 infos.put("count", parent.getChildren().getSize()); 590 591 if (pictureType.equals("resource")) 592 { 593 String resourceId = link.getResourcePictureId(); 594 pictureInfos.put("id", resourceId); 595 try 596 { 597 Resource resource = _resolver.resolveById(resourceId); 598 599 pictureInfos.put("filename", resource.getName()); 600 pictureInfos.put("size", resource.getLength()); 601 pictureInfos.put("type", "explorer"); 602 pictureInfos.put("lastModified", resource.getLastModified()); 603 604 String contextPath = ContextHelper.getRequest(_context).getContextPath(); 605 String viewUrl = contextPath + "/plugins/explorer/resource?id=" + resource.getId(); 606 String downloadUrl = viewUrl + "&download=true"; 607 pictureInfos.put("viewUrl", viewUrl); 608 pictureInfos.put("downloadUrl", downloadUrl); 609 } 610 catch (UnknownAmetysObjectException e) 611 { 612 getLogger().error("The resource of id'" + resourceId + "' does not exist anymore. The picture for link of id '" + link.getId() + "' will be ignored.", e); 613 infos.put("pictureNotFound", true); 614 } 615 } 616 else if (pictureType.equals("external")) 617 { 618 BinaryMetadata picMeta = link.getExternalPicture(); 619 620 pictureInfos.put("path", DefaultLink.PROPERTY_PICTURE); 621 pictureInfos.put("filename", picMeta.getFilename()); 622 pictureInfos.put("size", picMeta.getLength()); 623 pictureInfos.put("lastModified", picMeta.getLastModified()); 624 pictureInfos.put("type", "metadata"); 625 626 String contextPath = ContextHelper.getRequest(_context).getContextPath(); 627 String viewUrl = contextPath + "/plugins/cms/binaryMetadata/" + DefaultLink.PROPERTY_PICTURE + "?objectId=" + link.getId(); 628 String downloadUrl = viewUrl + "&download=true"; 629 pictureInfos.put("viewUrl", viewUrl); 630 pictureInfos.put("downloadUrl", downloadUrl); 631 632 } 633 else if (pictureType.equals("glyph")) 634 { 635 pictureInfos.put("id", link.getPictureGlyph()); 636 pictureInfos.put("type", "glyph"); 637 } 638 infos.put("picture", pictureInfos); 639 640 infos.put("grantAnyUser", String.valueOf(link.isAllowedAnyUser())); 641 642 boolean isAccessLimited = link.isAllowedAnyUser() || ArrayUtils.isNotEmpty(link.getGrantedUsers()) || ArrayUtils.isNotEmpty(link.getGrantedGroups()); 643 infos.put("isRestricted", isAccessLimited); 644 645 // Themes 646 List<Map<String, String>> themesList = new ArrayList<>(); 647 for (String themeId : link.getThemes()) 648 { 649 try 650 { 651 DefaultTheme theme = _resolver.resolveById(themeId); 652 Map<String, String> themeData = new HashMap<>(); 653 themeData.put("id", themeId); 654 themeData.put("label", theme.getLabel()); 655 themesList.add(themeData); 656 } 657 catch (UnknownAmetysObjectException e) 658 { 659 // Theme does not exist anymore 660 } 661 } 662 663 infos.put("themes", themesList); 664 665 List<Map<String, Object>> grantedUserList = new ArrayList<>(); 666 for (UserIdentity grantedUser : link.getGrantedUsers()) 667 { 668 User user = _userManager.getUser(grantedUser); 669 if (user != null) 670 { 671 grantedUserList.add(_userHelper.user2json(user)); 672 } 673 } 674 infos.put("grantedUsers", grantedUserList); 675 676 List<Map<String, String>> grantedGroupList = new ArrayList<>(); 677 for (GroupIdentity grantedGroup : link.getGrantedGroups()) 678 { 679 Group group = _groupManager.getGroup(grantedGroup); 680 if (group != null) 681 { 682 Map<String, String> groupData = new HashMap<>(); 683 groupData.put("groupId", grantedGroup.getId()); 684 groupData.put("groupDirectory", grantedGroup.getDirectoryId()); 685 groupData.put("label", group.getLabel()); 686 grantedGroupList.add(groupData); 687 } 688 } 689 infos.put("grantedGroups", grantedGroupList); 690 691 return infos; 692 } 693 694 /** 695 * Get links infos 696 * @param linkIds the link id 697 * @return the link infos 698 */ 699 @Callable 700 public List<Map<String, Object>> getLinks(List<String> linkIds) 701 { 702 List<Map<String, Object>> result = new ArrayList<>(); 703 704 for (String linkId: linkIds) 705 { 706 try 707 { 708 result.add(getLink(linkId)); 709 } 710 catch (UnknownAmetysObjectException e) 711 { 712 // does not exists 713 } 714 } 715 return result; 716 } 717 718 /** 719 * Test if a link with the specified url or internal url exists in the directory. 720 * @param url the url to test. 721 * @param internalUrl the internal url to test 722 * @param siteName the site name. 723 * @param language the language. 724 * @return true if the link exists. 725 * @throws AmetysRepositoryException if a repository error occurs. 726 */ 727 protected boolean _urlExists(String url, String internalUrl, String siteName, String language) throws AmetysRepositoryException 728 { 729 boolean externalLinkExists = false; 730 if (StringUtils.isNotBlank(url)) 731 { 732 String externalLinkXpathQuery = _directoryHelper.getUrlExistsQuery(siteName, language, url); 733 try (AmetysObjectIterable<DefaultLink> externalLinks = _resolver.query(externalLinkXpathQuery);) 734 { 735 externalLinkExists = externalLinks.iterator().hasNext(); 736 } 737 } 738 739 boolean internalLinkExists = false; 740 if (StringUtils.isNotBlank(internalUrl)) 741 { 742 String internalLinkXpathQuery = _directoryHelper.getUrlExistsQuery(siteName, language, internalUrl); 743 try (AmetysObjectIterable<DefaultLink> internalLinks = _resolver.query(internalLinkXpathQuery);) 744 { 745 internalLinkExists = internalLinks.iterator().hasNext(); 746 } 747 } 748 749 return externalLinkExists || internalLinkExists; 750 } 751 752 /** 753 * Test if a link with the specified url or internal url exists in the directory for the given user. 754 * @param url the url to test. 755 * @param internalUrl the internal url to test 756 * @param siteName the site name. 757 * @param language the language. 758 * @param user The user identity 759 * @return true if the link exists for the given user. 760 * @throws AmetysRepositoryException if a repository error occurs. 761 */ 762 protected boolean _urlExistsForUser(String url, String internalUrl, String siteName, String language, UserIdentity user) throws AmetysRepositoryException 763 { 764 boolean externalLinkExists = false; 765 if (StringUtils.isNotBlank(url)) 766 { 767 String externalLinkXpathQuery = _directoryHelper.getUrlExistsForUserQuery(siteName, language, url, user); 768 try (AmetysObjectIterable<DefaultLink> externalLinks = _resolver.query(externalLinkXpathQuery);) 769 { 770 externalLinkExists = externalLinks.iterator().hasNext(); 771 } 772 } 773 774 boolean internalLinkExists = false; 775 if (StringUtils.isNotBlank(internalUrl)) 776 { 777 String internalLinkXpathQuery = _directoryHelper.getUrlExistsForUserQuery(siteName, language, internalUrl, user); 778 try (AmetysObjectIterable<DefaultLink> internalLinks = _resolver.query(internalLinkXpathQuery);) 779 { 780 internalLinkExists = internalLinks.iterator().hasNext(); 781 } 782 } 783 784 return externalLinkExists || internalLinkExists; 785 } 786 787 /** 788 * Create the link object. 789 * @param name the desired link name. 790 * @param rootNode the links root node. 791 * @return the created Link. 792 */ 793 protected DefaultLink _createLink(String name, ModifiableTraversableAmetysObject rootNode) 794 { 795 String originalName = FilterNameHelper.filterName(name); 796 797 // Find unique name 798 String uniqueName = originalName; 799 int index = 2; 800 while (rootNode.hasChild(uniqueName)) 801 { 802 uniqueName = originalName + "-" + (index++); 803 } 804 805 return rootNode.createChild(uniqueName, DefaultLinkFactory.LINK_NODE_TYPE); 806 } 807 808 /** 809 * Move link the first position 810 * @param link the link to move 811 * @throws RepositoryException if an error occurs while exploring the repository 812 */ 813 private void _moveFirst(DefaultLink link) throws RepositoryException 814 { 815 try (AmetysObjectIterable<AmetysObject> children = ((TraversableAmetysObject) link.getParent()).getChildren();) 816 { 817 // Resolve the link in the same session or the linkRoot.saveChanges() call below won't see the order changes. 818 link.orderBefore(((TraversableAmetysObject) link.getParent()).getChildren().iterator().next()); 819 ((ModifiableAmetysObject) link.getParent()).saveChanges(); 820 } 821 } 822 823 /** 824 * Move link after its following 825 * @param link the link to move down 826 * @throws RepositoryException if an error occurs while exploring the repository 827 */ 828 private void _moveDown(DefaultLink link) throws RepositoryException 829 { 830 TraversableAmetysObject parentNode = link.getParent(); 831 AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren(); 832 Iterator<AmetysObject> it = siblings.iterator(); 833 834 boolean iterate = true; 835 while (it.hasNext() && iterate) 836 { 837 DefaultLink sibling = (DefaultLink) it.next(); 838 iterate = !sibling.getName().equals(link.getName()); 839 } 840 841 if (it.hasNext()) 842 { 843 // Move the link after his next sibling: move the next sibling before the link to move. 844 DefaultLink nextLink = (DefaultLink) it.next(); 845 nextLink.orderBefore(link); 846 847 link.saveChanges(); 848 } 849 } 850 851 /** 852 * Move link before its preceding 853 * @param link the link to move up 854 * @throws RepositoryException if an error occurs while exploring the repository 855 */ 856 private void _moveUp(DefaultLink link) throws RepositoryException 857 { 858 TraversableAmetysObject parentNode = link.getParent(); 859 AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren(); 860 Iterator<AmetysObject> it = siblings.iterator(); 861 DefaultLink previousLink = null; 862 863 while (it.hasNext()) 864 { 865 DefaultLink sibling = (DefaultLink) it.next(); 866 if (sibling.getName().equals(link.getName())) 867 { 868 break; 869 } 870 871 previousLink = sibling; 872 } 873 874 if (previousLink != null) 875 { 876 // Move the link after his next sibling: move the next sibling before the link to move. 877 link.orderBefore(previousLink); 878 link.saveChanges(); 879 } 880 } 881 882 /** 883 * Move link to the last position. 884 * @param link the link to move up 885 * @throws RepositoryException if an error occurs while exploring the repository 886 */ 887 private void _moveLast(DefaultLink link) throws RepositoryException 888 { 889 link.moveTo(link.getParent(), false); 890 ((ModifiableAmetysObject) link.getParent()).saveChanges(); 891 } 892}