001/* 002 * Copyright 2021 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.forms.dao; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023import java.util.function.Function; 024import java.util.stream.Collectors; 025import java.util.stream.Stream; 026 027import javax.jcr.Node; 028import javax.jcr.RepositoryException; 029 030import org.apache.avalon.framework.component.Component; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.avalon.framework.service.Serviceable; 034 035import org.ametys.core.observation.ObservationManager; 036import org.ametys.core.right.RightManager; 037import org.ametys.core.right.RightManager.RightResult; 038import org.ametys.core.ui.Callable; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.core.util.LambdaUtils; 042import org.ametys.plugins.forms.FormXpathUtils; 043import org.ametys.plugins.forms.repository.Form; 044import org.ametys.plugins.forms.repository.FormDirectory; 045import org.ametys.plugins.forms.repository.FormDirectoryFactory; 046import org.ametys.plugins.repository.AmetysObject; 047import org.ametys.plugins.repository.AmetysObjectIterable; 048import org.ametys.plugins.repository.AmetysObjectResolver; 049import org.ametys.plugins.repository.AmetysRepositoryException; 050import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 051import org.ametys.plugins.repository.MovableAmetysObject; 052import org.ametys.plugins.repository.UnknownAmetysObjectException; 053import org.ametys.plugins.repository.jcr.NameHelper; 054import org.ametys.runtime.plugin.component.AbstractLogEnabled; 055import org.ametys.web.repository.site.SiteManager; 056 057import com.google.common.base.Predicates; 058import com.google.common.collect.ImmutableMap; 059 060/** 061 * DAO for manipulating form directories 062 */ 063public class FormDirectoryDAO extends AbstractLogEnabled implements Serviceable, Component 064{ 065 /** The Avalon role */ 066 public static final String ROLE = FormDirectoryDAO.class.getName(); 067 068 /** The alias id of the root {@link FormDirectory} */ 069 public static final String ROOT_FORM_DIRECTORY_ID = "root"; 070 071 /** The right id to handle forms */ 072 public static final String HANDLE_FORM_DIRECTORIES_RIGHT_ID = "FormsDirectory_Rights_Directories"; 073 074 private static final String __ROOT_NODE_NAME = "ametys:forms"; 075 076 private static final String __PLUGIN_NODE_NAME = "forms"; 077 078 private static final String __FORMDIRECTORY_NAME_PREFIX = "formdirectory-"; 079 080 /** Observer manager. */ 081 protected ObservationManager _observationManager; 082 083 /** The site manager */ 084 protected SiteManager _siteManager; 085 086 /** The current user provider */ 087 protected CurrentUserProvider _userProvider; 088 089 /** The Ametys object resolver */ 090 protected AmetysObjectResolver _resolver; 091 092 /** The right manager */ 093 protected RightManager _rightManager; 094 095 /** The form DAO */ 096 protected FormDAO _formDAO; 097 098 public void service(ServiceManager manager) throws ServiceException 099 { 100 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 101 _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 102 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 103 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 104 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 105 _formDAO = (FormDAO) manager.lookup(FormDAO.ROLE); 106 } 107 108 /** 109 * Check if a user have write rights on a form directory 110 * @param userIdentity the user 111 * @param formDirectory the form directory 112 * @return true if the user have write rights on a form directory 113 */ 114 public boolean hasWriteRightOnFormDirectory(UserIdentity userIdentity, FormDirectory formDirectory) 115 { 116 return _rightManager.hasRight(userIdentity, HANDLE_FORM_DIRECTORIES_RIGHT_ID, formDirectory) == RightResult.RIGHT_ALLOW; 117 } 118 119 /** 120 * Check rights for a form element as ametys object 121 * @param formElement the form element as ametys object 122 */ 123 public void checkHandleFormDirectoriesRight(AmetysObject formElement) 124 { 125 UserIdentity user = _userProvider.getUser(); 126 if (!hasWriteRightOnFormDirectory(user, null)) 127 { 128 throw new IllegalAccessError("User '" + user + "' tried to handle form directories without convenient right [" + HANDLE_FORM_DIRECTORIES_RIGHT_ID + "]"); 129 } 130 } 131 132 /** 133 * Get the root plugin storage object. 134 * @param siteName the site's name 135 * @return the root plugin storage object. 136 * @throws AmetysRepositoryException if a repository error occurs. 137 */ 138 public FormDirectory getFormDirectoriesRootNode(String siteName) throws AmetysRepositoryException 139 { 140 try 141 { 142 return _getOrCreateRootNode(siteName); 143 } 144 catch (AmetysRepositoryException e) 145 { 146 throw new AmetysRepositoryException("Unable to get the forms root node", e); 147 } 148 } 149 150 private FormDirectory _getOrCreateRootNode(String siteName) throws AmetysRepositoryException 151 { 152 ModifiableTraversableAmetysObject pluginsNode = _siteManager.getSite(siteName).getRootPlugins(); 153 154 ModifiableTraversableAmetysObject pluginNode = (ModifiableTraversableAmetysObject) _getOrCreateNode(pluginsNode, __PLUGIN_NODE_NAME, "ametys:unstructured"); 155 156 return (FormDirectory) _getOrCreateNode(pluginNode, __ROOT_NODE_NAME, FormDirectoryFactory.FORM_DIRECTORY_NODETYPE); 157 } 158 159 private AmetysObject _getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 160 { 161 AmetysObject definitionsNode; 162 if (parentNode.hasChild(nodeName)) 163 { 164 definitionsNode = parentNode.getChild(nodeName); 165 } 166 else 167 { 168 definitionsNode = parentNode.createChild(nodeName, nodeType); 169 parentNode.saveChanges(); 170 } 171 return definitionsNode; 172 } 173 174 /** 175 * Get the root directory properties 176 * @param siteName the site's name 177 * @return The root directory properties 178 */ 179 @Callable 180 public Map<String, Object> getRootProperties(String siteName) 181 { 182 return getFormDirectoryProperties(getFormDirectoriesRootNode(siteName), false); 183 } 184 185 /** 186 * Get the form directory properties 187 * @param siteName the site's name 188 * @param id The form directory id. Can be {@link #ROOT_FORM_DIRECTORY_ID} for the root directory. 189 * @return The form directory properties 190 */ 191 @Callable 192 public Map<String, Object> getFormDirectoryProperties(String siteName, String id) 193 { 194 return getFormDirectoryProperties(getFormDirectory(siteName, id), true); 195 } 196 197 /** 198 * Get the form directory properties 199 * @param formDirectory The form directory 200 * @param withRights <code>true</code> to add rights to the properties 201 * @return The form directory properties 202 */ 203 public Map<String, Object> getFormDirectoryProperties(FormDirectory formDirectory, boolean withRights) 204 { 205 Map<String, Object> infos = new HashMap<>(); 206 207 infos.put("isForm", false); 208 infos.put("id", formDirectory.getId()); 209 infos.put("title", formDirectory.getTitle()); 210 infos.put("fullPath", getFormDirectoryPath(formDirectory, " > ")); 211 212 FormDirectory formDirectoriesRoot = getFormDirectoriesRootNode(formDirectory.getSiteName()); 213 String parentId = formDirectory.getParent().getId().equals(formDirectoriesRoot.getId()) ? ROOT_FORM_DIRECTORY_ID : formDirectory.getParent().getId(); 214 infos.put("parentId", parentId); 215 216 infos.put("hasChildren", formDirectory.getChildren().getSize() != 0); 217 218 UserIdentity currentUser = _userProvider.getUser(); 219 boolean canWriteParent = formDirectory.getParent() instanceof FormDirectory && hasWriteRightOnFormDirectory(currentUser, (FormDirectory) formDirectory.getParent()); 220 221 // Used to check if a user have the right to limit access to this directory 222 infos.put("canEditRight", hasRightAffectationRightOnFormDirectory(currentUser, formDirectory)); 223 224 if (withRights) 225 { 226 infos.put("canWriteParent", canWriteParent); 227 infos.put("rights", _getUserRights(formDirectory)); 228 } 229 else 230 { 231 boolean canRead = this.hasReadRightOnFormDirectory(currentUser, formDirectory); 232 boolean canWrite = this.hasWriteRightOnFormDirectory(currentUser, formDirectory); 233 234 // Used to check if a user have the right to rename this directory 235 infos.put("canRename", canWrite && canWriteParent); 236 237 // Used to check if a user have the right to add directories or forms inside this directory 238 infos.put("canWrite", canWrite); 239 240 // Used to check if a user have the right to delete and drag&drop this directory 241 infos.put("canEdit", canWrite && _hasWriteRightOnEachDescendant(currentUser, formDirectory) && canWriteParent); 242 243 // Used to filter directories 244 infos.put("displayForRead", canRead || canWrite || hasAnyReadableDescendant(currentUser, formDirectory) || hasAnyWritableDescendant(currentUser, formDirectory)); 245 infos.put("displayForWrite", canWrite || _hasAnyWriteDescendantFolder(currentUser, formDirectory)); 246 infos.put("displayForRights", canWrite || hasAnyAssignableDescendant(currentUser, formDirectory)); 247 } 248 249 return infos; 250 } 251 252 /** 253 * Get user rights for the given form directory 254 * @param formDirectory the form directory 255 * @return the set of rights 256 */ 257 protected Set<String> _getUserRights (FormDirectory formDirectory) 258 { 259 UserIdentity user = _userProvider.getUser(); 260 return _rightManager.getUserRights(user, formDirectory); 261 } 262 263 /** 264 * Get form directory with given id 265 * @param siteName the site name where to search in jcr 266 * @param id the directory id 267 * @return the form directory having given id 268 */ 269 public FormDirectory getFormDirectory(String siteName, String id) 270 { 271 return ROOT_FORM_DIRECTORY_ID.equals(id) 272 ? getFormDirectoriesRootNode(siteName) 273 : _resolver.resolveById(id); 274 } 275 276 /** 277 * Get the path of a form directory 278 * @param formDirectory the form directory 279 * @param separator the seperator to use from path 280 * @return the path in form's directory 281 */ 282 public String getFormDirectoryPath(FormDirectory formDirectory, String separator) 283 { 284 List<String> fullPath = new ArrayList<>(); 285 fullPath.add(formDirectory.getTitle()); 286 287 AmetysObject parent = formDirectory.getParent(); 288 while (parent instanceof FormDirectory parentDirectory && !isRoot(parentDirectory)) 289 { 290 fullPath.add(0, parentDirectory.getTitle()); 291 parent = parent.getParent(); 292 } 293 return String.join(separator, fullPath); 294 } 295 296 /** 297 * Determines if the form directory is the root directory 298 * @param formDirectory the form directory. Cannot be null. 299 * @return true if is the root directory 300 */ 301 public boolean isRoot(FormDirectory formDirectory) 302 { 303 return __ROOT_NODE_NAME.equals(formDirectory.getName()); 304 } 305 306 /** 307 * Creates a new {@link FormDirectory} 308 * @param siteName the site's name 309 * @param parentId The id of the parent. Use {@link #ROOT_FORM_DIRECTORY_ID} for the root directory. 310 * @param name The desired name for the new {@link FormDirectory} 311 * @return A result map 312 */ 313 @Callable 314 public Map<String, Object> createFormDirectory(String siteName, String parentId, String name) 315 { 316 Map<String, Object> results = new HashMap<>(); 317 318 FormDirectory parent = getFormDirectory(siteName, parentId); 319 checkHandleFormDirectoriesRight(parent); 320 321 // Find unique and legal name for node 322 String uniqueName = _findUniqueName(parent, name); 323 324 FormDirectory formDirectory = parent.createChild(uniqueName, FormDirectoryFactory.FORM_DIRECTORY_NODETYPE); 325 formDirectory.setTitle(name); 326 parent.saveChanges(); 327 328 results.put("id", formDirectory.getId()); 329 results.put("title", formDirectory.getTitle()); 330 results.put("parentId", parent.getId()); 331 332 return results; 333 } 334 335 private String _findUniqueName(FormDirectory parent, String name) 336 { 337 String legalName; 338 try 339 { 340 legalName = NameHelper.filterName(name); 341 } 342 catch (IllegalArgumentException e) 343 { 344 legalName = NameHelper.filterName(__FORMDIRECTORY_NAME_PREFIX + name); 345 } 346 347 // Find unique name from legal name 348 String uniqueName = legalName; 349 int index = 2; 350 while (parent.hasChild(uniqueName)) 351 { 352 uniqueName = legalName + "-" + (index++); 353 } 354 355 return uniqueName; 356 } 357 358 /** 359 * Renames a {@link FormDirectory} 360 * @param id The id of the form directory 361 * @param newName The new name of the directory 362 * @return A result map 363 */ 364 @Callable 365 public Map<String, Object> renameFormDirectory(String id, String newName) 366 { 367 FormDirectory directory = _resolver.resolveById(id); 368 checkHandleFormDirectoriesRight(directory); 369 370 Map<String, Object> results = new HashMap<>(); 371 372 AmetysObject parent = directory.getParent(); 373 374 if (parent instanceof FormDirectory parentDirectory) 375 { 376 UserIdentity currentUser = _userProvider.getUser(); 377 378 boolean canWrite = hasWriteRightOnFormDirectory(currentUser, directory); 379 boolean canWriteParent = hasWriteRightOnFormDirectory(currentUser, parentDirectory); 380 381 if (canWrite && canWriteParent) 382 { 383 // Find unique and legal name for node 384 String uniqueName = _findUniqueName(parentDirectory, newName); 385 386 Node node = directory.getNode(); 387 try 388 { 389 node.getSession().move(node.getPath(), node.getParent().getPath() + '/' + uniqueName); 390 directory.setTitle(newName); 391 node.getSession().save(); 392 393 results.put("id", id); 394 results.put("title", directory.getTitle()); 395 } 396 catch (RepositoryException e) 397 { 398 getLogger().warn("Form directory renaming failed.", e); 399 results.put("message", "cannot-rename"); 400 } 401 } 402 else 403 { 404 results.put("message", "not-allowed"); 405 } 406 } 407 else 408 { 409 results.put("message", "not-allowed"); 410 } 411 412 return results; 413 } 414 415 /** 416 * Deletes {@link FormDirectory}(s) 417 * @param ids The ids of the form directories to delete 418 * @return A result map 419 */ 420 @Callable 421 public Map<String, Object> deleteFormDirectory(List<String> ids) 422 { 423 Map<String, Object> results = new HashMap<>(); 424 425 List<Map<String, Object>> allDeleted = new ArrayList<>(); 426 List<Map<String, Object>> allUnknown = new ArrayList<>(); 427 428 if (!canDeleteAllFormDirectories(ids)) 429 { 430 results.put("message", "not-allowed"); 431 return results; 432 } 433 434 for (String id : ids) 435 { 436 try 437 { 438 FormDirectory directory = _resolver.resolveById(id); 439 String name = directory.getName(); 440 String title = directory.getTitle(); 441 directory.remove(); 442 directory.saveChanges(); 443 Map<String, Object> deleted = ImmutableMap.of("id", id, "name", name, "title", title); 444 allDeleted.add(deleted); 445 } 446 catch (UnknownAmetysObjectException e) 447 { 448 Map<String, Object> unknown = ImmutableMap.of("id", id); 449 allUnknown.add(unknown); 450 getLogger().error("Unable to delete form directory. The directory of id '" + id + " doesn't exist", e); 451 } 452 } 453 454 results.put("deleted", allDeleted); 455 results.put("unknown", allUnknown); 456 457 return results; 458 } 459 460 /** 461 * Moves a {@link FormDirectory} 462 * @param siteName name of the site 463 * @param id The id of the form directory 464 * @param newParentId The id of the new parent directory of the form directory. Use {@link #ROOT_FORM_DIRECTORY_ID} for the root directory. 465 * @return A result map 466 */ 467 @Callable 468 public Map<String, Object> moveFormDirectory(String siteName, String id, String newParentId) 469 { 470 Map<String, Object> results = new HashMap<>(); 471 FormDirectory formDirectory = _resolver.resolveById(id); 472 FormDirectory parentFormDirectory = getFormDirectory(siteName, newParentId); 473 UserIdentity currentUser = _userProvider.getUser(); 474 475 if (_hasWriteRightOnEachDescendant(currentUser, formDirectory) 476 && hasWriteRightOnFormDirectory(currentUser, parentFormDirectory)) 477 { 478 move(formDirectory, siteName, newParentId, results); 479 } 480 else 481 { 482 results.put("message", "not-allowed"); 483 } 484 results.put("id", formDirectory.getId()); 485 results.put("title", formDirectory.getTitle()); 486 return results; 487 } 488 489 /** 490 * Moves a {@link FormDirectory} or a {@link Form} 491 * @param obj form or directory to move 492 * @param siteName name of the site 493 * @param newParentId id of directory where to drop obj 494 * @param results a result map 495 */ 496 public void move(MovableAmetysObject obj, String siteName, String newParentId, Map<String, Object> results) 497 { 498 FormDirectory newParent = getFormDirectory(siteName, newParentId); 499 if (obj.canMoveTo(newParent)) 500 { 501 try 502 { 503 obj.moveTo(newParent, false); 504 } 505 catch (AmetysRepositoryException e) 506 { 507 getLogger().warn("Form moving failed.", e); 508 results.put("message", "cannot-move"); 509 } 510 } 511 else 512 { 513 results.put("message", "cannot-move"); 514 } 515 } 516 517 /** 518 * Determines if application must warn before deleting the given {@link FormDirectory}s 519 * @param ids The {@link FormDirectory} ids 520 * @return <code>true</code> if application must warn 521 */ 522 @Callable 523 public boolean mustWarnBeforeDeletion(List<String> ids) 524 { 525 return ids.stream() 526 .anyMatch(LambdaUtils.wrapPredicate(this::_mustWarnBeforeDeletion)); 527 } 528 529 private boolean _mustWarnBeforeDeletion(String id) 530 { 531 FormDirectory directory = _resolver.resolveById(id); 532 AmetysObjectIterable<Form> allForms = getChildFormsForAdministrator(directory, false); 533 return _containsNotOwnForms(allForms); 534 } 535 536 private boolean _containsNotOwnForms(AmetysObjectIterable<Form> allForms) 537 { 538 UserIdentity currentUser = _userProvider.getUser(); 539 return allForms.stream() 540 .map(Form::getAuthor) 541 .anyMatch(Predicates.not(currentUser::equals)); 542 } 543 544 /** 545 * Can the current user delete all the {@link FormDirectory}s from the list ? 546 * @param ids The {@link FormDirectory} ids 547 * @return <code>true</code> if he can 548 */ 549 protected boolean canDeleteAllFormDirectories(List<String> ids) 550 { 551 return canDeleteFormDirectories(ids).values() 552 .stream() 553 .allMatch(Boolean.TRUE::equals); 554 } 555 556 /** 557 * Determines if the current user can delete the given {@link FormDirectory}s 558 * @param ids The {@link FormDirectory} ids 559 * @return A map with <code>true</code> for each id if the current user can delete the associated {@link FormDirectory} 560 */ 561 @Callable 562 public Map<String, Boolean> canDeleteFormDirectories(List<String> ids) 563 { 564 return ids.stream() 565 .collect(Collectors.toMap( 566 Function.identity(), 567 LambdaUtils.wrap(this::_canDeleteFormDirectory))); 568 } 569 570 private boolean _canDeleteFormDirectory(String id) 571 { 572 if (ROOT_FORM_DIRECTORY_ID.equals(id)) 573 { 574 return false; 575 } 576 else 577 { 578 UserIdentity user = _userProvider.getUser(); 579 FormDirectory directory = _resolver.resolveById(id); 580 581 return _hasWriteRightOnEachDescendant(user, directory) 582 && !_hasPublishedForms(directory) 583 && directory.getParent() instanceof FormDirectory 584 && hasWriteRightOnFormDirectory(user, (FormDirectory) directory.getParent()); 585 } 586 } 587 588 private boolean _hasPublishedForms(FormDirectory formDirectory) 589 { 590 boolean hasPublishedForms = false; 591 for (AmetysObject child : formDirectory.getChildren()) 592 { 593 if (child instanceof FormDirectory) 594 { 595 hasPublishedForms = hasPublishedForms || _hasPublishedForms((FormDirectory) child); 596 } 597 else if (child instanceof Form) 598 { 599 hasPublishedForms = hasPublishedForms || !_formDAO.getFormPage(child.getId(), formDirectory.getSiteName()).isEmpty(); 600 } 601 } 602 603 return hasPublishedForms; 604 } 605 606 /** 607 * Check if a user have write rights on an form directory and each of his descendant 608 * @param userIdentity the user 609 * @param formDirectory the form directory 610 * @return true if the user have write rights on an form directory and each of his descendant 611 */ 612 private boolean _hasWriteRightOnEachDescendant(UserIdentity userIdentity, FormDirectory formDirectory) 613 { 614 boolean hasRight = hasWriteRightOnFormDirectory(userIdentity, formDirectory); 615 if (!hasRight) 616 { 617 return false; 618 } 619 620 try (AmetysObjectIterable<AmetysObject> children = formDirectory.getChildren()) 621 { 622 for (AmetysObject child : children) 623 { 624 if (child instanceof FormDirectory) 625 { 626 hasRight = hasRight && _hasWriteRightOnEachDescendant(userIdentity, (FormDirectory) child); 627 } 628 else if (child instanceof Form) 629 { 630 hasRight = hasRight && hasWriteRightOnFormAndParent(userIdentity, (Form) child, formDirectory); 631 } 632 633 if (!hasRight) 634 { 635 return false; 636 } 637 } 638 639 return hasRight; 640 } 641 } 642 643 /** 644 * Check if a user have write rights on a form 645 * @param userIdentity the user 646 * @param form the source for the form 647 * @param formDirectory the form directory 648 * @return true if the user have write rights on a form 649 */ 650 public boolean hasWriteRightOnFormAndParent(UserIdentity userIdentity, Form form, FormDirectory formDirectory) 651 { 652 return form != null && userIdentity.equals(form.getAuthor()) || _formDAO.hasWriteRightOnForm(userIdentity, form) && hasWriteRightOnFormDirectory(userIdentity, formDirectory); 653 } 654 655 /** 656 * Gets all forms in WRITE access for given parent 657 * @param parent The {@link FormDirectory}, defining the context from which getting children 658 * @param onlyDirect <code>true</code> in order to have only direct child forms from parent path, <code>false</code> otherwise to have all forms at any level underneath the parent path 659 * @param user The user 660 * @return all forms in WRITE access for given parent 661 */ 662 public Stream<Form> getChildFormsInWriteAccess(FormDirectory parent, boolean onlyDirect, UserIdentity user) 663 { 664 return _resolver.query(FormXpathUtils.getXPathForForms(parent, onlyDirect)) 665 .stream() 666 .filter(Form.class::isInstance) 667 .map(obj -> (Form) obj) 668 .filter(form -> _formDAO.hasWriteRightOnForm(user, form)); 669 } 670 671 /** 672 * Gets all forms in WRITE access for given parent 673 * @param parent The {@link FormDirectory}, defining the context from which getting children 674 * @param onlyDirect <code>true</code> in order to have only direct child forms from parent path, <code>false</code> otherwise to have all forms at any level underneath the parent path 675 * @param user The user 676 * @return all forms in WRITE access for given parent 677 */ 678 public Stream<Form> getChildFormsInRightAccess(FormDirectory parent, boolean onlyDirect, UserIdentity user) 679 { 680 return _resolver.query(FormXpathUtils.getXPathForForms(parent, onlyDirect)) 681 .stream() 682 .filter(Form.class::isInstance) 683 .map(obj -> (Form) obj) 684 .filter(form -> _formDAO.hasRightAffectationRightOnForm(user, form)); 685 } 686 687 /** 688 * Gets all forms in READ access for given parent 689 * @param parent The {@link FormDirectory}, defining the context from which getting children 690 * @param onlyDirect <code>true</code> in order to have only direct child forms from parent path, <code>false</code> otherwise to have all forms at any level underneath the parent path 691 * @param user The user 692 * @return all forms in READ access for given parent 693 */ 694 public Stream<Form> getChildFormsInReadAccess(FormDirectory parent, boolean onlyDirect, UserIdentity user) 695 { 696 return _resolver.query(FormXpathUtils.getXPathForForms(parent, onlyDirect)) 697 .stream() 698 .filter(Form.class::isInstance) 699 .map(obj -> (Form) obj) 700 .filter(form -> _formDAO.hasReadRightOnForm(user, form)); 701 } 702 703 /** 704 * Gets all forms for administrator for given parent 705 * @param parent The {@link FormDirectory}, defining the context from which getting children 706 * @param onlyDirect <code>true</code> in order to have only direct child forms from parent path, <code>false</code> otherwise to have all forms at any level underneath the parent path 707 * @return all forms for administrator for given parent 708 */ 709 public AmetysObjectIterable<Form> getChildFormsForAdministrator(FormDirectory parent, boolean onlyDirect) 710 { 711 return _resolver.query(FormXpathUtils.getXPathForForms(parent, onlyDirect)); 712 } 713 714 /** 715 * Gets all form directories for given parent 716 * @param parent The {@link FormDirectory}, defining the context from which getting children 717 * @return all form directories for given parent 718 */ 719 public AmetysObjectIterable<FormDirectory> getChildFormDirectories(FormDirectory parent) 720 { 721 return _resolver.query(FormXpathUtils.getXPathForFormDirectories(parent)); 722 } 723 724 /** 725 * Provides the current user. 726 * @return the user which cannot be <code>null</code>. 727 */ 728 protected UserIdentity _getCurrentUser() 729 { 730 return _userProvider.getUser(); 731 } 732 733 /** 734 * Check if a user have write rights on a form directory 735 * @param userIdentity the user 736 * @param formDirectory the form directory 737 * @return true if the user have write rights on a form 738 */ 739 public boolean hasRightAffectationRightOnFormDirectory(UserIdentity userIdentity, FormDirectory formDirectory) 740 { 741 return hasWriteRightOnFormDirectory(userIdentity, formDirectory) || _rightManager.hasRight(userIdentity, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW; 742 } 743 744 745 /** 746 * Check if a user have read rights on a form directory 747 * @param userIdentity the user 748 * @param directory the form directory 749 * @return true if the user have read rights on a form directory 750 */ 751 public boolean hasReadRightOnFormDirectory(UserIdentity userIdentity, FormDirectory directory) 752 { 753 return _rightManager.hasReadAccess(userIdentity, directory); 754 } 755 756 /** 757 * Check if a directory have a descendant in read access for a given user 758 * @param userIdentity the user 759 * @param formDirectory the form directory 760 * @return true if the user have read right for at least one child of this directory 761 */ 762 public Boolean hasAnyReadableDescendant(UserIdentity userIdentity, FormDirectory formDirectory) 763 { 764 boolean hasDescendant = hasReadRightOnFormDirectory(userIdentity, formDirectory); 765 if (hasDescendant) 766 { 767 return true; 768 } 769 770 try (AmetysObjectIterable<AmetysObject> children = formDirectory.getChildren()) 771 { 772 for (AmetysObject child : children) 773 { 774 if (child instanceof FormDirectory) 775 { 776 hasDescendant = hasDescendant || hasAnyReadableDescendant(userIdentity, (FormDirectory) child); 777 } 778 else if (child instanceof Form) 779 { 780 hasDescendant = hasDescendant || _formDAO.hasReadRightOnForm(userIdentity, (Form) child); 781 } 782 783 if (hasDescendant) 784 { 785 return true; 786 } 787 } 788 789 return hasDescendant; 790 } 791 } 792 793 /** 794 * Check if a directory have descendant in write access for a given user 795 * @param userIdentity the user 796 * @param formDirectory the form directory 797 * @return true if the user have write right for at least one child of this directory 798 */ 799 public Boolean hasAnyWritableDescendant(UserIdentity userIdentity, FormDirectory formDirectory) 800 { 801 boolean hasDescendant = hasWriteRightOnFormDirectory(userIdentity, formDirectory); 802 if (hasDescendant) 803 { 804 return true; 805 } 806 807 try (AmetysObjectIterable<AmetysObject> children = formDirectory.getChildren()) 808 { 809 for (AmetysObject child : children) 810 { 811 if (child instanceof FormDirectory) 812 { 813 hasDescendant = hasDescendant || hasAnyWritableDescendant(userIdentity, (FormDirectory) child); 814 } 815 else if (child instanceof Form) 816 { 817 hasDescendant = hasDescendant || hasWriteRightOnFormAndParent(userIdentity, (Form) child, formDirectory); 818 } 819 820 if (hasDescendant) 821 { 822 return true; 823 } 824 } 825 } 826 827 return hasDescendant; 828 } 829 830 private Boolean _hasAnyWriteDescendantFolder(UserIdentity userIdentity, FormDirectory formDirectory) 831 { 832 boolean hasDescendant = hasWriteRightOnFormDirectory(userIdentity, formDirectory); 833 if (hasDescendant) 834 { 835 return true; 836 } 837 838 try (AmetysObjectIterable<AmetysObject> children = formDirectory.getChildren()) 839 { 840 for (AmetysObject child : children) 841 { 842 if (child instanceof FormDirectory) 843 { 844 hasDescendant = hasDescendant || _hasAnyWriteDescendantFolder(userIdentity, (FormDirectory) child); 845 } 846 847 if (hasDescendant) 848 { 849 return true; 850 } 851 } 852 853 return hasDescendant; 854 } 855 } 856 857 /** 858 * Check if a directory have descendant in right assignment access for a given user 859 * @param userIdentity the user 860 * @param formDirectory the form directory 861 * @return true if the user have right assignment right for at least one child of this directory 862 */ 863 public Boolean hasAnyAssignableDescendant(UserIdentity userIdentity, FormDirectory formDirectory) 864 { 865 boolean hasDescendant = hasRightAffectationRightOnFormDirectory(userIdentity, formDirectory); 866 if (hasDescendant) 867 { 868 return true; 869 } 870 871 try (AmetysObjectIterable<AmetysObject> children = formDirectory.getChildren()) 872 { 873 for (AmetysObject child : children) 874 { 875 if (child instanceof FormDirectory) 876 { 877 hasDescendant = hasDescendant || hasAnyAssignableDescendant(userIdentity, (FormDirectory) child); 878 } 879 else if (child instanceof Form) 880 { 881 hasDescendant = hasDescendant || _formDAO.hasRightAffectationRightOnForm(userIdentity, (Form) child); 882 } 883 884 if (hasDescendant) 885 { 886 return true; 887 } 888 } 889 890 return hasDescendant; 891 } 892 } 893}