001/* 002 * Copyright 2012 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.repository; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.time.ZonedDateTime; 021 022import javax.jcr.Node; 023import javax.jcr.PathNotFoundException; 024import javax.jcr.RepositoryException; 025import javax.jcr.Value; 026 027import org.apache.commons.lang3.ArrayUtils; 028import org.apache.commons.lang3.StringUtils; 029 030import org.ametys.cms.data.Binary; 031import org.ametys.plugins.linkdirectory.Link; 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.DefaultTraversableAmetysObject; 038import org.ametys.web.repository.SiteAwareAmetysObject; 039import org.ametys.web.repository.site.Site; 040 041/** 042 * Repository implementation of a directory link. 043 */ 044public class DefaultLink extends DefaultTraversableAmetysObject<DefaultLinkFactory> implements Link, SiteAwareAmetysObject, MovableAmetysObject 045{ 046 /** Constant for URL property. */ 047 public static final String PROPERTY_URL = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":url"; 048 049 /** Constant for id of provider of dynamic information. */ 050 public static final String PROPERTY_DYNAMIC_INFO_PROVIDER = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":dynamic-information"; 051 052 /** Constant for internal URL property. */ 053 public static final String PROPERTY_INTERNAL_URL = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":internal-url"; 054 055 /** Constant for URL type property. */ 056 public static final String PROPERTY_URLTYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":url-type"; 057 058 /** Constant for title property. */ 059 public static final String PROPERTY_TITLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":title"; 060 061 /** Constant for title property. */ 062 public static final String PROPERTY_CONTENT = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":content"; 063 064 /** Constant for URL alternative property. */ 065 public static final String PROPERTY_URL_ALTERNATIVE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":url-alternative"; 066 067 /** Constant for picture type property. */ 068 public static final String PROPERTY_PICTURE_TYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":picture-type"; 069 070 /** Constant for picture data property. */ 071 public static final String PROPERTY_PICTURE = "picture"; 072 073 /** Constant for picture id property. */ 074 public static final String PROPERTY_PICTURE_ID = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":picture-id"; 075 076 /** Constant for picture glyph property. */ 077 public static final String PROPERTY_PICTURE_GLYPH = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":picture-glyph"; 078 079 /** Constant for picture alternative property. */ 080 public static final String PROPERTY_PICTURE_ALTERNATIVE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":picture-alternative"; 081 082 /** Constant for themes property. */ 083 public static final String PROPERTY_THEMES = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":themes"; 084 085 /** Constant for color property. */ 086 public static final String PROPERTY_COLOR = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":color"; 087 088 /** Constant for page property. */ 089 public static final String PROPERTY_PAGE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":page"; 090 091 /** Constant for status property. */ 092 public static final String PROPERTY_STATUS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":status"; 093 094 /** Constant for default visibilty property. */ 095 public static final String PROPERTY_DEFAULT_VISIBILITY = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":default-visibility"; 096 097 098 /** 099 * Create a {@link DefaultLink}. 100 * 101 * @param node the node backing this {@link AmetysObject}. 102 * @param parentPath the parent path in the Ametys hierarchy. 103 * @param factory the {@link DefaultLinkFactory} which creates the 104 * AmetysObject. 105 */ 106 public DefaultLink(Node node, String parentPath, DefaultLinkFactory factory) 107 { 108 super(node, parentPath, factory); 109 } 110 111 @Override 112 public String getUrl() throws AmetysRepositoryException 113 { 114 try 115 { 116 return getNode().getProperty(PROPERTY_URL).getString(); 117 } 118 catch (PathNotFoundException e) 119 { 120 return null; 121 } 122 catch (RepositoryException e) 123 { 124 throw new AmetysRepositoryException("Error getting the URL property.", e); 125 } 126 } 127 128 @Override 129 public void setUrl(LinkType urlType, String url) throws AmetysRepositoryException 130 { 131 try 132 { 133 getNode().setProperty(PROPERTY_URLTYPE, urlType.toString()); 134 getNode().setProperty(PROPERTY_URL, url); 135 } 136 catch (RepositoryException e) 137 { 138 throw new AmetysRepositoryException("Error setting the URL property.", e); 139 } 140 } 141 142 @Override 143 public String getInternalUrl() throws AmetysRepositoryException 144 { 145 try 146 { 147 return getNode().getProperty(PROPERTY_INTERNAL_URL).getString(); 148 } 149 catch (PathNotFoundException e) 150 { 151 return null; 152 } 153 catch (RepositoryException e) 154 { 155 throw new AmetysRepositoryException("Error getting the internal URL property.", e); 156 } 157 } 158 159 @Override 160 public void setInternalUrl(String url) throws AmetysRepositoryException 161 { 162 try 163 { 164 getNode().setProperty(PROPERTY_INTERNAL_URL, url); 165 } 166 catch (RepositoryException e) 167 { 168 throw new AmetysRepositoryException("Error setting the internal URL property.", e); 169 } 170 } 171 172 @Override 173 public LinkType getUrlType() throws AmetysRepositoryException 174 { 175 try 176 { 177 return LinkType.valueOf(getNode().getProperty(PROPERTY_URLTYPE).getString()); 178 } 179 catch (RepositoryException e) 180 { 181 throw new AmetysRepositoryException("Unable to get URL type property", e); 182 } 183 } 184 185 @Override 186 public String getTitle() throws AmetysRepositoryException 187 { 188 try 189 { 190 return getNode().getProperty(PROPERTY_TITLE).getString(); 191 } 192 catch (PathNotFoundException e) 193 { 194 return null; 195 } 196 catch (RepositoryException e) 197 { 198 throw new AmetysRepositoryException("Error getting the title property.", e); 199 } 200 } 201 202 @Override 203 public void setTitle(String title) throws AmetysRepositoryException 204 { 205 try 206 { 207 getNode().setProperty(PROPERTY_TITLE, title); 208 } 209 catch (RepositoryException e) 210 { 211 throw new AmetysRepositoryException("Error setting the title property.", e); 212 } 213 } 214 215 @Override 216 public String getContent() throws AmetysRepositoryException 217 { 218 try 219 { 220 return getNode().getProperty(PROPERTY_CONTENT).getString(); 221 } 222 catch (PathNotFoundException e) 223 { 224 return null; 225 } 226 catch (RepositoryException e) 227 { 228 throw new AmetysRepositoryException("Error getting the content property.", e); 229 } 230 } 231 232 @Override 233 public void setContent(String content) throws AmetysRepositoryException 234 { 235 try 236 { 237 getNode().setProperty(PROPERTY_CONTENT, content); 238 } 239 catch (RepositoryException e) 240 { 241 throw new AmetysRepositoryException("Error setting the content property.", e); 242 } 243 } 244 245 @Override 246 public String getAlternative() throws AmetysRepositoryException 247 { 248 try 249 { 250 return getNode().getProperty(PROPERTY_URL_ALTERNATIVE).getString(); 251 } 252 catch (PathNotFoundException e) 253 { 254 return null; 255 } 256 catch (RepositoryException e) 257 { 258 throw new AmetysRepositoryException("Error getting the alternative property.", e); 259 } 260 } 261 262 @Override 263 public void setAlternative(String alternative) throws AmetysRepositoryException 264 { 265 try 266 { 267 getNode().setProperty(PROPERTY_URL_ALTERNATIVE, alternative); 268 } 269 catch (RepositoryException e) 270 { 271 throw new AmetysRepositoryException("Error setting the alternative property.", e); 272 } 273 } 274 275 @Override 276 public Binary getExternalPicture() throws AmetysRepositoryException 277 { 278 return getValue(PROPERTY_PICTURE); 279 } 280 281 @Override 282 public void setExternalPicture(String mimeType, String filename, InputStream stream) throws AmetysRepositoryException 283 { 284 try 285 { 286 removePictureMetas(); 287 288 setPictureType("external"); 289 290 Binary pic = new Binary(); 291 292 pic.setLastModificationDate(ZonedDateTime.now()); 293 pic.setInputStream(stream); 294 pic.setMimeType(mimeType); 295 pic.setFilename(filename); 296 297 setValue(PROPERTY_PICTURE, pic); 298 } 299 catch (IOException | RepositoryException e) 300 { 301 throw new AmetysRepositoryException("Error setting the external picture property.", e); 302 } 303 } 304 305 @Override 306 public String getResourcePictureId() throws AmetysRepositoryException 307 { 308 try 309 { 310 return getNode().getProperty(PROPERTY_PICTURE_ID).getString(); 311 } 312 catch (PathNotFoundException e) 313 { 314 return null; 315 } 316 catch (RepositoryException e) 317 { 318 throw new AmetysRepositoryException("Error getting the picture ID property.", e); 319 } 320 } 321 322 @Override 323 public void setResourcePicture(String resourceId) throws AmetysRepositoryException 324 { 325 try 326 { 327 removePictureMetas(); 328 329 setPictureType("resource"); 330 331 getNode().setProperty(PROPERTY_PICTURE_ID, resourceId); 332 } 333 catch (RepositoryException e) 334 { 335 throw new AmetysRepositoryException("Error setting the alternative property.", e); 336 } 337 } 338 339 @Override 340 public void setNoPicture() throws AmetysRepositoryException 341 { 342 try 343 { 344 setPictureType(""); 345 346 removePictureMetas(); 347 } 348 catch (RepositoryException e) 349 { 350 throw new AmetysRepositoryException("Error setting the alternative property.", e); 351 } 352 } 353 354 @Override 355 public String getPictureType() throws AmetysRepositoryException 356 { 357 try 358 { 359 return getNode().getProperty(PROPERTY_PICTURE_TYPE).getString(); 360 } 361 catch (RepositoryException e) 362 { 363 throw new AmetysRepositoryException("Error getting the type property.", e); 364 } 365 } 366 367 @Override 368 public void setPictureType(String type) throws AmetysRepositoryException 369 { 370 try 371 { 372 getNode().setProperty(PROPERTY_PICTURE_TYPE, type); 373 } 374 catch (RepositoryException e) 375 { 376 throw new AmetysRepositoryException("Error setting the type property.", e); 377 } 378 } 379 380 public String getPictureGlyph() throws AmetysRepositoryException 381 { 382 try 383 { 384 return getNode().getProperty(PROPERTY_PICTURE_GLYPH).getString(); 385 } 386 catch (RepositoryException e) 387 { 388 throw new AmetysRepositoryException("Error getting the picture glyph property.", e); 389 } 390 } 391 392 public void setPictureGlyph(String glyph) throws AmetysRepositoryException 393 { 394 try 395 { 396 setPictureType("glyph"); 397 398 getNode().setProperty(PROPERTY_PICTURE_GLYPH, glyph); 399 } 400 catch (RepositoryException e) 401 { 402 throw new AmetysRepositoryException("Error setting the picture glyph property.", e); 403 } 404 } 405 406 @Override 407 public String getPictureAlternative() throws AmetysRepositoryException 408 { 409 try 410 { 411 return getNode().getProperty(PROPERTY_PICTURE_ALTERNATIVE).getString(); 412 } 413 catch (PathNotFoundException e) 414 { 415 return null; 416 } 417 catch (RepositoryException e) 418 { 419 throw new AmetysRepositoryException("Error getting the alternative property.", e); 420 } 421 } 422 423 @Override 424 public void setPictureAlternative(String alternative) throws AmetysRepositoryException 425 { 426 try 427 { 428 getNode().setProperty(PROPERTY_PICTURE_ALTERNATIVE, alternative); 429 } 430 catch (RepositoryException e) 431 { 432 throw new AmetysRepositoryException("Error setting the alternative property.", e); 433 } 434 } 435 436 @Override 437 public String[] getThemes() throws AmetysRepositoryException 438 { 439 return _getListFromMetadataName(PROPERTY_THEMES); 440 } 441 442 @Override 443 public void setThemes(String[] themes) throws AmetysRepositoryException 444 { 445 _setListWithMetadataName(themes, PROPERTY_THEMES); 446 } 447 448 @Override 449 public void removeTheme(String themeId) throws AmetysRepositoryException 450 { 451 try 452 { 453 String[] themes = getThemes(); 454 String[] updatedThemes = ArrayUtils.removeElement(themes, themeId); 455 getNode().setProperty(PROPERTY_THEMES, updatedThemes); 456 } 457 catch (RepositoryException e) 458 { 459 throw new AmetysRepositoryException("Error removing theme of id " + themeId, e); 460 } 461 } 462 463 @Override 464 public Site getSite() throws AmetysRepositoryException 465 { 466 AmetysObject parent = getParent(); 467 while (parent != null) 468 { 469 if (parent instanceof Site) 470 { 471 return (Site) parent; 472 } 473 parent = parent.getParent(); 474 } 475 476 return null; 477 } 478 479 @Override 480 public String getSiteName() throws AmetysRepositoryException 481 { 482 return getSite().getName(); 483 } 484 485 /** 486 * Get the theme language. 487 * 488 * @return the theme language. 489 */ 490 public String getLanguage() 491 { 492 return getParent().getParent().getName(); 493 } 494 495 /** 496 * Remove all picture metas (picture ID and picture binary). 497 * 498 * @throws RepositoryException if an error occurs. 499 */ 500 protected void removePictureMetas() throws RepositoryException 501 { 502 getNode().setProperty(PROPERTY_PICTURE_ID, StringUtils.EMPTY); 503 removeValue(PROPERTY_PICTURE); 504 } 505 506 @Override 507 public void orderBefore(AmetysObject siblingObject) throws AmetysRepositoryException 508 { 509 if (siblingObject instanceof DefaultLink) 510 { 511 DefaultLink sibling = (DefaultLink) siblingObject; 512 sibling.getPath(); 513 Node node = getNode(); 514 String siblingPath = ""; 515 try 516 { 517 siblingPath = sibling.getNode().getPath(); 518 if (siblingPath.contains("/")) 519 { 520 siblingPath = StringUtils.substringAfterLast(siblingPath, "/"); 521 } 522 String path = node.getPath(); 523 if (path.contains("/")) 524 { 525 path = StringUtils.substringAfterLast(path, "/"); 526 } 527 node.getParent().orderBefore(path, siblingPath); 528 } 529 catch (RepositoryException e) 530 { 531 throw new AmetysRepositoryException(String.format("Unable to order link '%s' before link '%s'", this, siblingPath), e); 532 } 533 } 534 else 535 { 536 throw new AmetysRepositoryException("A link can only be moved before another link."); 537 } 538 } 539 540 @Override 541 public void moveTo(AmetysObject newParent, boolean renameIfExist) throws AmetysRepositoryException, RepositoryIntegrityViolationException 542 { 543 if (getParent().equals(newParent)) 544 { 545 try 546 { 547 // Just order current node to the end. 548 Node node = getNode(); 549 String path = node.getPath(); 550 if (path.contains("/")) 551 { 552 path = StringUtils.substringAfterLast(path, "/"); 553 } 554 node.getParent().orderBefore(path, null); 555 } 556 catch (RepositoryException e) 557 { 558 throw new AmetysRepositoryException(String.format("Unable to move link '%s' outside the root link node.", this), e); 559 } 560 } 561 else 562 { 563 throw new AmetysRepositoryException("A link can only be moved before another link."); 564 } 565 } 566 567 @Override 568 public boolean canMoveTo(AmetysObject newParent) throws AmetysRepositoryException 569 { 570 return getParent().equals(newParent); 571 } 572 573 /** 574 * Retrieves the list of values corresponding to the metadata name passed as 575 * parameter 576 * 577 * @param metadataName the name of the metadata to retrieve 578 * @return the list corresponding to the metadata name 579 */ 580 private String[] _getListFromMetadataName(String metadataName) 581 { 582 try 583 { 584 if (getNode().hasProperty(metadataName)) 585 { 586 Value[] values = getNode().getProperty(metadataName).getValues(); 587 588 String[] list = new String[values.length]; 589 590 for (int i = 0; i < values.length; i++) 591 { 592 list[i] = values[i].getString(); 593 } 594 595 return list; 596 } 597 else 598 { 599 return new String[0]; 600 } 601 } 602 catch (RepositoryException e) 603 { 604 throw new AmetysRepositoryException("An error occurred while trying to get the property '" + metadataName + "'.", e); 605 } 606 } 607 608 /** 609 * Sets the list of values to the node corresponding to the metadata name 610 * passed as a parameter 611 * 612 * @param list the list of value to be set 613 * @param metadataName the name of the metadata 614 */ 615 private void _setListWithMetadataName(String[] list, String metadataName) 616 { 617 try 618 { 619 getNode().setProperty(metadataName, list); 620 } 621 catch (RepositoryException e) 622 { 623 throw new AmetysRepositoryException("An error occurred while trying to set the property '" + metadataName + "'.", e); 624 } 625 } 626 627 @Override 628 public String getDynamicInformationProvider() throws AmetysRepositoryException 629 { 630 try 631 { 632 return getNode().getProperty(PROPERTY_DYNAMIC_INFO_PROVIDER).getString(); 633 } 634 catch (PathNotFoundException e) 635 { 636 return null; 637 } 638 catch (RepositoryException e) 639 { 640 throw new AmetysRepositoryException("Error getting the URL property.", e); 641 } 642 } 643 644 @Override 645 public void setDynamicInformationProvider(String providerId) throws AmetysRepositoryException 646 { 647 try 648 { 649 getNode().setProperty(PROPERTY_DYNAMIC_INFO_PROVIDER, providerId); 650 } 651 catch (RepositoryException e) 652 { 653 throw new AmetysRepositoryException("Error setting the URL property.", e); 654 } 655 } 656 657 @Override 658 public String getColor() throws AmetysRepositoryException 659 { 660 try 661 { 662 return getNode().getProperty(PROPERTY_COLOR).getString(); 663 } 664 catch (PathNotFoundException e) 665 { 666 return null; 667 } 668 catch (RepositoryException e) 669 { 670 throw new AmetysRepositoryException("Error getting the color property.", e); 671 } 672 } 673 674 @Override 675 public void setColor(String color) throws AmetysRepositoryException 676 { 677 try 678 { 679 getNode().setProperty(PROPERTY_COLOR, color); 680 } 681 catch (RepositoryException e) 682 { 683 throw new AmetysRepositoryException("Error setting the color property.", e); 684 } 685 } 686 687 @Override 688 public String getPage() throws AmetysRepositoryException 689 { 690 try 691 { 692 return getNode().getProperty(PROPERTY_PAGE).getString(); 693 } 694 catch (PathNotFoundException e) 695 { 696 return null; 697 } 698 catch (RepositoryException e) 699 { 700 throw new AmetysRepositoryException("Error getting the page property.", e); 701 } 702 } 703 704 @Override 705 public void setPage(String page) throws AmetysRepositoryException 706 { 707 try 708 { 709 getNode().setProperty(PROPERTY_PAGE, page); 710 } 711 catch (RepositoryException e) 712 { 713 throw new AmetysRepositoryException("Error setting the page property.", e); 714 } 715 } 716 717 @Override 718 public LinkStatus getStatus() throws AmetysRepositoryException 719 { 720 try 721 { 722 return LinkStatus.valueOf(getNode().getProperty(PROPERTY_STATUS).getString()); 723 } 724 catch (PathNotFoundException e) 725 { 726 return null; 727 } 728 catch (RepositoryException e) 729 { 730 throw new AmetysRepositoryException("Error getting the status property.", e); 731 } 732 } 733 734 @Override 735 public void setStatus(LinkStatus status) throws AmetysRepositoryException 736 { 737 try 738 { 739 getNode().setProperty(PROPERTY_STATUS, status.name()); 740 } 741 catch (RepositoryException e) 742 { 743 throw new AmetysRepositoryException("Error setting the status property.", e); 744 } 745 } 746 747 @Override 748 public LinkVisibility getDefaultVisibility() throws AmetysRepositoryException 749 { 750 try 751 { 752 Node node = getNode(); 753 if (node.hasProperty(PROPERTY_DEFAULT_VISIBILITY)) 754 { 755 return LinkVisibility.valueOf(getNode().getProperty(PROPERTY_DEFAULT_VISIBILITY).getString()); 756 } 757 else 758 { 759 return LinkVisibility.VISIBLE; 760 } 761 } 762 catch (RepositoryException e) 763 { 764 throw new AmetysRepositoryException("Error getting the visibility property.", e); 765 } 766 } 767 768 @Override 769 public void setDefaultVisibility(LinkVisibility visibility) throws AmetysRepositoryException 770 { 771 try 772 { 773 getNode().setProperty(PROPERTY_DEFAULT_VISIBILITY, visibility.name()); 774 } 775 catch (RepositoryException e) 776 { 777 throw new AmetysRepositoryException("Error setting the visibility property.", e); 778 } 779 } 780}