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