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