001/* 002 * Copyright 2013 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.queriesdirectory; 017 018import java.util.Date; 019import java.util.GregorianCalendar; 020import java.util.HashSet; 021import java.util.Set; 022 023import javax.jcr.Node; 024import javax.jcr.NodeIterator; 025import javax.jcr.RepositoryException; 026 027import org.apache.commons.collections.CollectionUtils; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.core.group.GroupIdentity; 031import org.ametys.core.user.UserIdentity; 032import org.ametys.plugins.repository.AmetysObject; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.MovableAmetysObject; 035import org.ametys.plugins.repository.RepositoryConstants; 036import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 037import org.ametys.plugins.repository.jcr.DefaultAmetysObject; 038import org.ametys.runtime.i18n.I18nizableText; 039 040/** 041 * Class representing a query, backed by a JCR node.<br> 042 */ 043public class Query extends DefaultAmetysObject<QueryFactory> implements MovableAmetysObject 044{ 045 /** Property name for query title */ 046 public static final String TITLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":label"; 047 /** Property name for query type */ 048 public static final String TYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":type"; 049 /** Property name for query description */ 050 public static final String DESCRIPTION = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":description"; 051 /** Property name for query content */ 052 public static final String CONTENT = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":content"; 053 /** Property name for query author */ 054 public static final String AUTHOR = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":author"; 055 /** Property name for query last modification date */ 056 public static final String LASTMODIFICATIONDATE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":lastModificationDate"; 057 058 /** Property name for query visibility */ 059 public static final String VISIBILITY = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":visibility"; 060 /** Property name for query read access */ 061 public static final String PROFILE_READ_ACCESS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":read-access"; 062 /** Property name for query write access */ 063 public static final String PROFILE_WRITE_ACCESS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":write-access"; 064 /** Property name for query groups */ 065 public static final String GROUPS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":groups"; 066 /** Property name for query users */ 067 public static final String USERS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":users"; 068 069 /** 070 * Visibility of a Query 071 */ 072 public enum Visibility 073 { 074 /** Private visibility. */ 075 PRIVATE, 076 /** Shared visibility. */ 077 SHARED, 078 /** Public visibility. */ 079 PUBLIC; 080 081 @Override 082 public String toString() 083 { 084 return name().toLowerCase(); 085 } 086 } 087 088 /** 089 * Type of a Query 090 */ 091 public enum Type 092 { 093 /** Simple type. */ 094 SIMPLE, 095 /** Default type. */ 096 ADVANCED, 097 /** Script type. */ 098 SCRIPT; 099 100 @Override 101 public String toString() 102 { 103 return name().toLowerCase(); 104 } 105 } 106 107 /** 108 * Rights profiles 109 */ 110 public enum QueryProfile 111 { 112 /** Read access */ 113 READ_ACCESS, 114 /** Write access */ 115 WRITE_ACCESS; 116 117 @Override 118 public String toString() 119 { 120 return name().toLowerCase(); 121 } 122 123 /** 124 * Query profile title getter. 125 * @return The title of the profile 126 */ 127 public I18nizableText getTitle() 128 { 129 switch (this) 130 { 131 case READ_ACCESS: 132 return new I18nizableText("plugin.queries-directory", "PLUGINS_QUERIESDIRECTORY_RIGHT_PROFILE_READ_TITLE"); 133 case WRITE_ACCESS: 134 return new I18nizableText("plugin.queries-directory", "PLUGINS_QUERIESDIRECTORY_RIGHT_PROFILE_WRITE_TITLE"); 135 default: 136 return null; 137 } 138 } 139 140 /** 141 * Query profile description getter. 142 * @return The description of the profile 143 */ 144 public I18nizableText getDescription() 145 { 146 switch (this) 147 { 148 case READ_ACCESS: 149 return new I18nizableText("plugin.queries-directory", "PLUGINS_QUERIESDIRECTORY_RIGHT_PROFILE_READ_DESCRIPTION"); 150 case WRITE_ACCESS: 151 return new I18nizableText("plugin.queries-directory", "PLUGINS_QUERIESDIRECTORY_RIGHT_PROFILE_READ_DESCRIPTION"); 152 default: 153 return null; 154 } 155 } 156 } 157 158 /** 159 * Creates an {@link Query}. 160 * @param node the node backing this {@link AmetysObject} 161 * @param parentPath the parentPath in the Ametys hierarchy 162 * @param factory the DefaultAmetysObjectFactory which created the AmetysObject 163 */ 164 public Query(Node node, String parentPath, QueryFactory factory) 165 { 166 super(node, parentPath, factory); 167 } 168 169 170 /** 171 * Set the title of this query. 172 * @param title the description 173 */ 174 public void setTitle (String title) 175 { 176 try 177 { 178 getNode().setProperty(TITLE, title != null ? title : StringUtils.EMPTY); 179 } 180 catch (RepositoryException e) 181 { 182 throw new AmetysRepositoryException("Error while setting title property.", e); 183 } 184 } 185 186 /** 187 * Set the type of this query. 188 * @param type the description 189 */ 190 public void setType (String type) 191 { 192 try 193 { 194 getNode().setProperty(TYPE, type != null ? type : StringUtils.EMPTY); 195 } 196 catch (RepositoryException e) 197 { 198 throw new AmetysRepositoryException("Error while setting type property.", e); 199 } 200 } 201 202 /** 203 * Set the description of this query. 204 * @param description the description 205 */ 206 public void setDescription (String description) 207 { 208 try 209 { 210 getNode().setProperty(DESCRIPTION, description != null ? description : StringUtils.EMPTY); 211 } 212 catch (RepositoryException e) 213 { 214 throw new AmetysRepositoryException("Error while setting description property.", e); 215 } 216 } 217 218 /** 219 * Set the content of this query. 220 * @param content the content 221 */ 222 public void setContent (String content) 223 { 224 try 225 { 226 getNode().setProperty(CONTENT, content != null ? content : StringUtils.EMPTY); 227 } 228 catch (RepositoryException e) 229 { 230 throw new AmetysRepositoryException("Error while setting content property.", e); 231 } 232 } 233 234 /** 235 * Set the author of this query. 236 * @param author the author 237 */ 238 public void setAuthor(UserIdentity author) 239 { 240 try 241 { 242 Node authorNode = _getOrCreateNode(getNode(), AUTHOR, "ametys:user"); 243 authorNode.setProperty("ametys:login", author.getLogin()); 244 authorNode.setProperty("ametys:population", author.getPopulationId()); 245 } 246 catch (RepositoryException e) 247 { 248 throw new AmetysRepositoryException("Error setting the author property.", e); 249 } 250 } 251 252 /** 253 * Set the date of the last modification. 254 * @param lastModificationDate the last modification date to set. 255 */ 256 public void setLastModificationDate(Date lastModificationDate) 257 { 258 try 259 { 260 GregorianCalendar cal = new GregorianCalendar(); 261 cal.setTime(lastModificationDate); 262 263 getNode().setProperty(LASTMODIFICATIONDATE, cal); 264 } 265 catch (RepositoryException e) 266 { 267 throw new AmetysRepositoryException("Error setting the last modification date property.", e); 268 } 269 } 270 271 /** 272 * Set the query visibility 273 * @param visibility the visibility 274 */ 275 public void setVisibility (Visibility visibility) 276 { 277 try 278 { 279 getNode().setProperty(VISIBILITY, visibility.name()); 280 } 281 catch (RepositoryException e) 282 { 283 throw new AmetysRepositoryException("Error while setting title property.", e); 284 } 285 } 286 287 /** 288 * Get the title of the query 289 * @return The title 290 */ 291 public String getTitle() 292 { 293 try 294 { 295 if (getNode().hasProperty(TITLE)) 296 { 297 return getNode().getProperty(TITLE).getValue().getString(); 298 } 299 else 300 { 301 return StringUtils.EMPTY; 302 } 303 } 304 catch (RepositoryException e) 305 { 306 throw new AmetysRepositoryException("Error while getting title property.", e); 307 } 308 } 309 310 /** 311 * Get the type of the query 312 * @return The type 313 */ 314 public String getType() 315 { 316 try 317 { 318 if (getNode().hasProperty(TYPE)) 319 { 320 return getNode().getProperty(TYPE).getValue().getString(); 321 } 322 else 323 { 324 return StringUtils.EMPTY; 325 } 326 } 327 catch (RepositoryException e) 328 { 329 throw new AmetysRepositoryException("Error while getting type property.", e); 330 } 331 } 332 333 334 /** 335 * Get the description of the query 336 * @return The description 337 */ 338 public String getDescription() 339 { 340 try 341 { 342 if (getNode().hasProperty(DESCRIPTION)) 343 { 344 return getNode().getProperty(DESCRIPTION).getValue().getString(); 345 } 346 else 347 { 348 return StringUtils.EMPTY; 349 } 350 } 351 catch (RepositoryException e) 352 { 353 throw new AmetysRepositoryException("Error while getting description property.", e); 354 } 355 } 356 357 /** 358 * Get the content of the query 359 * @return The content 360 */ 361 public String getContent() 362 { 363 try 364 { 365 if (getNode().hasProperty(CONTENT)) 366 { 367 return getNode().getProperty(CONTENT).getValue().getString(); 368 } 369 else 370 { 371 return StringUtils.EMPTY; 372 } 373 } 374 catch (RepositoryException e) 375 { 376 throw new AmetysRepositoryException("Error while getting content property.", e); 377 } 378 } 379 380 381 /** 382 * Get the author of the query 383 * @return The author 384 */ 385 public UserIdentity getAuthor () 386 { 387 try 388 { 389 Node authorNode = getNode().getNode(AUTHOR); 390 return new UserIdentity(authorNode.getProperty("ametys:login").getString(), authorNode.getProperty("ametys:population").getString()); 391 } 392 catch (RepositoryException e) 393 { 394 throw new AmetysRepositoryException("Error while getting author property.", e); 395 } 396 } 397 398 /** 399 * Get the visibility of the query 400 * @return The visibility 401 */ 402 public Visibility getVisibility() 403 { 404 try 405 { 406 return Visibility.valueOf(getNode().getProperty(VISIBILITY).getValue().getString()); 407 } 408 catch (RepositoryException e) 409 { 410 throw new AmetysRepositoryException("Error while getting visibility property.", e); 411 } 412 } 413 414 /** 415 * Get the granted users 416 * @param profile teh query profile 417 * @return the logins of granted users 418 */ 419 public Set<UserIdentity> getGrantedUsers(QueryProfile profile) 420 { 421 try 422 { 423 String profileNodeName = null; 424 425 switch (profile) 426 { 427 case READ_ACCESS: 428 profileNodeName = PROFILE_READ_ACCESS; 429 break; 430 case WRITE_ACCESS: 431 profileNodeName = PROFILE_WRITE_ACCESS; 432 break; 433 default: 434 throw new AmetysRepositoryException("Unexisting Query profile : " + profile); 435 } 436 437 Node profileNode = _getOrCreateNode(getNode(), profileNodeName, "nt:unstructured"); 438 Set<UserIdentity> grantedUsers = new HashSet<>(); 439 440 grantedUsers.add(this.getAuthor()); 441 442 if (profileNode.hasNode(USERS)) 443 { 444 NodeIterator it = profileNode.getNodes(USERS); 445 while (it.hasNext()) 446 { 447 Node next = (Node) it.next(); 448 grantedUsers.add(new UserIdentity(next.getProperty("ametys:login").getString(), next.getProperty("ametys:population").getString())); 449 } 450 } 451 452 return grantedUsers; 453 } 454 catch (RepositoryException e) 455 { 456 throw new AmetysRepositoryException("Error while getting grantedUsers property.", e); 457 } 458 } 459 460 /** 461 * Get the granted groups 462 * @param profile the query profile 463 * @return the granted groups 464 */ 465 public Set<GroupIdentity> getGrantedGroups(QueryProfile profile) 466 { 467 try 468 { 469 String profileNodeName = null; 470 471 switch (profile) 472 { 473 case READ_ACCESS: 474 profileNodeName = PROFILE_READ_ACCESS; 475 break; 476 case WRITE_ACCESS: 477 profileNodeName = PROFILE_WRITE_ACCESS; 478 break; 479 default: 480 throw new AmetysRepositoryException("Unexisting Query profile : " + profile); 481 } 482 483 Node profileNode = _getOrCreateNode(getNode(), profileNodeName, "nt:unstructured"); 484 Set<GroupIdentity> grantedGroups = new HashSet<>(); 485 486 if (profileNode.hasNode(GROUPS)) 487 { 488 NodeIterator it = profileNode.getNodes(GROUPS); 489 while (it.hasNext()) 490 { 491 Node next = (Node) it.next(); 492 grantedGroups.add(new GroupIdentity(next.getProperty("ametys:groupId").getString(), next.getProperty("ametys:groupDirectory").getString())); 493 } 494 } 495 496 return grantedGroups; 497 } 498 catch (RepositoryException e) 499 { 500 throw new AmetysRepositoryException("Error while getting grantedGroups property.", e); 501 } 502 } 503 504 /** 505 * Determines if an user has READ access to this query. 506 * @param user The user 507 * @return <code>true</code> if the user has read access, <code>false</code> otherwise 508 */ 509 public boolean canRead(UserIdentity user) 510 { 511 Visibility visibility = getVisibility(); 512 513 switch (visibility) 514 { 515 case PRIVATE: 516 return getAuthor().equals(user); 517 518 case SHARED: 519 return getGrantedUsers(QueryProfile.READ_ACCESS).contains(user) || CollectionUtils.containsAny(getGrantedGroups(QueryProfile.READ_ACCESS), _getFactory()._getGroupManager().getUserGroups(user)) || canWrite(user); 520 521 case PUBLIC: 522 default: 523 return true; 524 } 525 } 526 527 /** 528 * Determines if an user has WRITE access to this query. 529 * @param user The user 530 * @return <code>true</code> if the user has write access, <code>false</code> otherwise 531 */ 532 public boolean canWrite(UserIdentity user) 533 { 534 Visibility visibility = getVisibility(); 535 536 switch (visibility) 537 { 538 case PRIVATE: 539 return getAuthor().equals(user); 540 541 case SHARED: 542 return getGrantedUsers(QueryProfile.WRITE_ACCESS).contains(user) || CollectionUtils.containsAny(getGrantedGroups(QueryProfile.WRITE_ACCESS), _getFactory()._getGroupManager().getUserGroups(user)); 543 544 case PUBLIC: 545 default: 546 return true; 547 } 548 } 549 550 /** 551 * Set the granted users 552 * @param profile the query profile 553 * @param users the granted users 554 */ 555 public void setGrantedUsers (QueryProfile profile, Set<UserIdentity> users) 556 { 557 try 558 { 559 String profileNodeName = null; 560 561 switch (profile) 562 { 563 case READ_ACCESS: 564 profileNodeName = PROFILE_READ_ACCESS; 565 break; 566 case WRITE_ACCESS: 567 profileNodeName = PROFILE_WRITE_ACCESS; 568 break; 569 default: 570 throw new AmetysRepositoryException("Unexisting query profile : " + profile); 571 } 572 573 Node profileNode = _getOrCreateNode(getNode(), profileNodeName, "nt:unstructured"); 574 575 NodeIterator it = profileNode.getNodes(USERS); 576 while (it.hasNext()) 577 { 578 Node next = (Node) it.next(); 579 next.remove(); 580 } 581 582 UserIdentity author = this.getAuthor(); 583 users.remove(author); 584 585 for (UserIdentity userIdentity : users) 586 { 587 Node userNode = profileNode.addNode(USERS, "ametys:user"); 588 userNode.setProperty("ametys:login", userIdentity.getLogin()); 589 userNode.setProperty("ametys:population", userIdentity.getPopulationId()); 590 } 591 } 592 catch (RepositoryException e) 593 { 594 throw new AmetysRepositoryException("Error while setting users property.", e); 595 } 596 } 597 598 /** 599 * Set the granted groups 600 * @param profile the query profile 601 * @param groups the granted groups 602 */ 603 public void setGrantedGroups(QueryProfile profile, Set<GroupIdentity> groups) 604 { 605 try 606 { 607 String profileNodeName = null; 608 609 switch (profile) 610 { 611 case READ_ACCESS: 612 profileNodeName = PROFILE_READ_ACCESS; 613 break; 614 case WRITE_ACCESS: 615 profileNodeName = PROFILE_WRITE_ACCESS; 616 break; 617 default: 618 throw new AmetysRepositoryException("Unexisting query profile : " + profile); 619 } 620 621 Node profileNode = _getOrCreateNode(getNode(), profileNodeName, "nt:unstructured"); 622 623 NodeIterator it = profileNode.getNodes(GROUPS); 624 while (it.hasNext()) 625 { 626 Node next = (Node) it.next(); 627 next.remove(); 628 } 629 630 for (GroupIdentity groupIdentity : groups) 631 { 632 Node groupNode = profileNode.addNode(GROUPS, "ametys:group"); 633 groupNode.setProperty("ametys:groupId", groupIdentity.getId()); 634 groupNode.setProperty("ametys:groupDirectory", groupIdentity.getDirectoryId()); 635 } 636 } 637 catch (RepositoryException e) 638 { 639 throw new AmetysRepositoryException("Error while setting groups property.", e); 640 } 641 } 642 643 private Node _getOrCreateNode(Node parent, String name, String type) throws RepositoryException 644 { 645 Node node = null; 646 647 if (parent.hasNode(name)) 648 { 649 node = parent.getNode(name); 650 } 651 else if (type != null) 652 { 653 node = parent.addNode(name, type); 654 } 655 else 656 { 657 node = parent.addNode(name); 658 } 659 660 return node; 661 } 662 663 /** 664 * Get the date of the last modification of the query 665 * @return The date 666 */ 667 public Date getLastModificationDate() 668 { 669 try 670 { 671 Date value = null; 672 673 if (getNode().hasProperty(LASTMODIFICATIONDATE)) 674 { 675 return getNode().getProperty(LASTMODIFICATIONDATE).getValue().getDate().getTime(); 676 } 677 678 return value; 679 } 680 catch (RepositoryException e) 681 { 682 throw new AmetysRepositoryException("Error getting the last modification date property.", e); 683 } 684 } 685 686 @Override 687 public boolean canMoveTo(AmetysObject newParent) throws AmetysRepositoryException 688 { 689 return QueryAndContainerCommonMethods.canMoveTo(newParent, this, _getFactory().getQueryDAO()); 690 } 691 692 @Override 693 public void moveTo(AmetysObject newParent, boolean renameIfExist) throws AmetysRepositoryException, RepositoryIntegrityViolationException 694 { 695 QueryAndContainerCommonMethods.moveTo(newParent, renameIfExist, this); 696 } 697 698 @Override 699 public void orderBefore(AmetysObject siblingNode) throws AmetysRepositoryException 700 { 701 QueryAndContainerCommonMethods.orderBefore(siblingNode, this); 702 } 703}