001/* 002 * Copyright 2010 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.web.repository.site; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.time.ZonedDateTime; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024import java.util.Optional; 025 026import javax.jcr.ItemExistsException; 027import javax.jcr.Node; 028import javax.jcr.NodeIterator; 029import javax.jcr.Property; 030import javax.jcr.PropertyIterator; 031import javax.jcr.RepositoryException; 032 033import org.apache.commons.lang3.StringUtils; 034import org.xml.sax.ContentHandler; 035import org.xml.sax.SAXException; 036 037import org.ametys.cms.data.Binary; 038import org.ametys.cms.data.ametysobject.ModifiableModelAwareDataAwareAmetysObject; 039import org.ametys.cms.data.holder.ModifiableIndexableDataHolder; 040import org.ametys.cms.data.holder.impl.DefaultModifiableModelAwareDataHolder; 041import org.ametys.cms.repository.Content; 042import org.ametys.cms.repository.ModifiableContent; 043import org.ametys.plugins.repository.AmetysObject; 044import org.ametys.plugins.repository.AmetysObjectIterable; 045import org.ametys.plugins.repository.AmetysRepositoryException; 046import org.ametys.plugins.repository.CollectionIterable; 047import org.ametys.plugins.repository.CopiableAmetysObject; 048import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 049import org.ametys.plugins.repository.MovableAmetysObject; 050import org.ametys.plugins.repository.RepositoryConstants; 051import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 052import org.ametys.plugins.repository.TraversableAmetysObject; 053import org.ametys.plugins.repository.UnknownAmetysObjectException; 054import org.ametys.plugins.repository.activities.ActivityHolder; 055import org.ametys.plugins.repository.activities.ActivityHolderAmetysObject; 056import org.ametys.plugins.repository.activities.DefaultActivityHolder; 057import org.ametys.plugins.repository.collection.AmetysObjectCollection; 058import org.ametys.plugins.repository.data.UnknownDataException; 059import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 060import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 061import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData; 062import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject; 063import org.ametys.plugins.repository.jcr.NodeHelper; 064import org.ametys.plugins.repository.jcr.SimpleAmetysObject; 065import org.ametys.web.pageaccess.RestrictedPagePolicy; 066import org.ametys.web.repository.content.WebContent; 067import org.ametys.web.repository.page.ModifiableZoneItem; 068import org.ametys.web.repository.page.ZoneItem; 069import org.ametys.web.repository.sitemap.Sitemap; 070 071/** 072 * {@link AmetysObject} for storing site informations. 073 */ 074public final class Site extends DefaultTraversableAmetysObject<SiteFactory> implements MovableAmetysObject, CopiableAmetysObject, ModifiableModelAwareDataAwareAmetysObject, ActivityHolderAmetysObject 075{ 076 /** Site node type name. */ 077 public static final String NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":site"; 078 079 /** Parameter name for project's site illustration */ 080 public static final String ILLUSTRATION_PARAMETER = "illustration"; 081 082 /** Constants for title metadata. */ 083 private static final String __TITLE_PARAMETER = "title"; 084 /** Constants for type metadata. */ 085 private static final String __TYPE_PARAMETER = "type"; 086 /** Constants for description metadata. */ 087 private static final String __DESCRIPTION_PARAMETER = "description"; 088 /** Constants for URL metadata. */ 089 private static final String __URL_PARAMETER = "url"; 090 /** Constants for color metadata. */ 091 private static final String __COLOR_PARAMETER = "color"; 092 /** Constants for skin metadata. */ 093 private static final String __SKIN_PARAMETER = "skin"; 094 /** Constants for restricted page policy metadata. */ 095 private static final String __DISPLAY_RESTRICTED_PAGES_PARAMETER = "display-restricted-pages"; 096 /** Constants for sitemaps node name. */ 097 private static final String __SITEMAPS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":sitemaps"; 098 /** Constants for contents node name. */ 099 private static final String __CONTENTS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":contents"; 100 /** Constants for resources node name. */ 101 private static final String __RESOURCES_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":resources"; 102 /** Constants for plugins node name. */ 103 private static final String __PLUGINS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":plugins"; 104 105 /** 106 * Creates a {@link Site}. 107 * @param node the node backing this {@link AmetysObject}. 108 * @param parentPath the parent path in the Ametys hierarchy. 109 * @param factory the {@link SiteFactory} which creates the AmetysObject. 110 */ 111 public Site(Node node, String parentPath, SiteFactory factory) 112 { 113 super(node, parentPath, factory); 114 } 115 116 /** 117 * Retrieves the title. 118 * @return the title. 119 * @throws AmetysRepositoryException if an error occurs. 120 */ 121 public String getTitle() throws AmetysRepositoryException 122 { 123 return (String) getValue(__TITLE_PARAMETER); 124 } 125 126 /** 127 * Retrieves the type. 128 * @return the type. 129 * @throws AmetysRepositoryException if an error occurs. 130 */ 131 public String getType() throws AmetysRepositoryException 132 { 133 try 134 { 135 // return default site type for null metadata for 3.0 compatibility 136 RepositoryData repositoryData = new JCRRepositoryData(getNode()); 137 return repositoryData.getString(__TYPE_PARAMETER); 138 } 139 catch (UnknownDataException me) 140 { 141 return SiteType.DEFAULT_SITE_TYPE_ID; 142 } 143 } 144 145 146 /** 147 * Set the type. 148 * @param type the type. 149 * @throws AmetysRepositoryException if another error occurs. 150 */ 151 public void setType(String type) throws AmetysRepositoryException 152 { 153 ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode()); 154 repositoryData.setValue(__TYPE_PARAMETER, type); 155 } 156 157 /** 158 * Set the title. 159 * @param title the title. 160 * @throws AmetysRepositoryException if another error occurs. 161 */ 162 public void setTitle(String title) throws AmetysRepositoryException 163 { 164 setValue(__TITLE_PARAMETER, title); 165 } 166 167 /** 168 * Retrieves the description. 169 * @return the description. 170 * @throws AmetysRepositoryException if an error occurs. 171 */ 172 public String getDescription() throws AmetysRepositoryException 173 { 174 return getValue(__DESCRIPTION_PARAMETER); 175 } 176 177 /** 178 * Set the description. 179 * @param description the description. 180 * @throws AmetysRepositoryException if an error occurs. 181 */ 182 public void setDescription(String description) throws AmetysRepositoryException 183 { 184 setValue(__DESCRIPTION_PARAMETER, description); 185 } 186 187 /** 188 * Retrieves the main url. 189 * @return the main url. 190 * @throws AmetysRepositoryException if an error occurs. 191 */ 192 public String getUrl() throws AmetysRepositoryException 193 { 194 String[] aliases = getUrlAliases(); 195 196 return aliases != null && aliases.length > 0 ? aliases[0] : null; 197 } 198 199 /** 200 * Retrieves the url aliases. 201 * @return the url. 202 * @throws AmetysRepositoryException if an error occurs. 203 */ 204 public String[] getUrlAliases() throws AmetysRepositoryException 205 { 206 String url = getValue(__URL_PARAMETER); 207 if (url == null) 208 { 209 return new String[0]; 210 } 211 else 212 { 213 String[] aliases = StringUtils.split(url, ','); 214 215 for (int i = 0; i < aliases.length; i++) 216 { 217 aliases[i] = aliases[i].trim(); 218 } 219 220 return aliases; 221 } 222 } 223 224 /** 225 * Set the url. 226 * @param url the url. 227 * @throws AmetysRepositoryException if an error occurs. 228 */ 229 public void setUrl(String url) throws AmetysRepositoryException 230 { 231 setValue(__URL_PARAMETER, url); 232 } 233 234 /** 235 * Retrieves the color 236 * @return The color. 237 * @throws AmetysRepositoryException if an error occurs. 238 */ 239 public String getColor() throws AmetysRepositoryException 240 { 241 return getValue(__COLOR_PARAMETER, false, StringUtils.EMPTY); 242 } 243 244 245 /** 246 * Retrieves a sitemap. 247 * @param sitemapName the sitemap name. 248 * @return the sitemap found. 249 * @throws AmetysRepositoryException if an error occurs. 250 * @throws UnknownAmetysObjectException if the object does not exist. 251 */ 252 public Sitemap getSitemap(String sitemapName) throws AmetysRepositoryException, UnknownAmetysObjectException 253 { 254 return ((TraversableAmetysObject) getChild(__SITEMAPS_NODE_NAME)).getChild(sitemapName); 255 } 256 257 /** 258 * Determines the existence of a sitemap 259 * @param lang The sitemap language 260 * @return true if the sitemap exists 261 * @throws AmetysRepositoryException if an error occurred 262 */ 263 public boolean hasSitemap (String lang) throws AmetysRepositoryException 264 { 265 DefaultTraversableAmetysObject<?> sitemaps = getChild(__SITEMAPS_NODE_NAME); 266 return sitemaps.hasChild(lang); 267 } 268 269 /** 270 * Add a sitemap language to the site 271 * @param lang The sitemap language 272 * @return The created sitemap 273 * @throws AmetysRepositoryException if an error occurred or if the sitemap already exists 274 */ 275 public Sitemap addSitemap (String lang) throws AmetysRepositoryException 276 { 277 DefaultTraversableAmetysObject<?> sitemaps = getChild(__SITEMAPS_NODE_NAME); 278 if (sitemaps.hasChild(lang)) 279 { 280 throw new AmetysRepositoryException ("The sitemap '" + lang + "' already exists"); 281 } 282 return sitemaps.createChild(lang, "ametys:sitemap"); 283 } 284 285 /** 286 * Retrieves sitemaps. 287 * @return the sitemaps or an empty {@link AmetysObjectIterable}. 288 * @throws AmetysRepositoryException if an error occurs. 289 */ 290 public AmetysObjectIterable<Sitemap> getSitemaps() throws AmetysRepositoryException 291 { 292 return ((TraversableAmetysObject) getChild(__SITEMAPS_NODE_NAME)).getChildren(); 293 } 294 295 /** 296 * Retrieves root contents. 297 * @return the root for contents 298 * @throws AmetysRepositoryException if an error occurs. 299 */ 300 public ModifiableTraversableAmetysObject getRootContents() throws AmetysRepositoryException 301 { 302 return getChild(__CONTENTS_NODE_NAME); 303 } 304 305 /** 306 * Retrieves contents. 307 * @return the contents or an empty {@link AmetysObjectIterable}. 308 * @throws AmetysRepositoryException if an error occurs. 309 */ 310 public AmetysObjectIterable<Content> getContents() throws AmetysRepositoryException 311 { 312 return ((TraversableAmetysObject) getChild(__CONTENTS_NODE_NAME)).getChildren(); 313 } 314 315 /** 316 * Retrieves root resources. 317 * @return the root for resources 318 * @throws AmetysRepositoryException if an error occurs. 319 */ 320 public ModifiableTraversableAmetysObject getRootResources() throws AmetysRepositoryException 321 { 322 return getChild(__RESOURCES_NODE_NAME); 323 } 324 325 /** 326 * Retrieves resources. 327 * @return the resources or an empty {@link AmetysObjectIterable}. 328 * @throws AmetysRepositoryException if an error occurs. 329 */ 330 public AmetysObjectIterable<AmetysObject> getResources() throws AmetysRepositoryException 331 { 332 return ((TraversableAmetysObject) getChild(__RESOURCES_NODE_NAME)).getChildren(); 333 } 334 335 /** 336 * Get the root for plugins 337 * @return the root for plugins 338 * @throws AmetysRepositoryException if an error occurs. 339 */ 340 public ModifiableTraversableAmetysObject getRootPlugins () throws AmetysRepositoryException 341 { 342 return (ModifiableTraversableAmetysObject) getChild(__PLUGINS_NODE_NAME); 343 } 344 345 /** 346 * Set the skin id for this site. 347 * @param skinId ths skin id 348 */ 349 public void setSkinId(String skinId) 350 { 351 setValue(__SKIN_PARAMETER, skinId); 352 } 353 354 /** 355 * Returns the skin id for this site. 356 * @return the skin id for this site. 357 */ 358 public String getSkinId() 359 { 360 return getValue(__SKIN_PARAMETER); 361 } 362 363 /** 364 * Returns the parent site, if any. 365 * @return the parent site, if any. 366 */ 367 public Site getParentSite() 368 { 369 AmetysObject parent = getParent(); 370 if (SiteManager.ROOT_SITES_PATH.equals(parent.getPath())) 371 { 372 return null; 373 } 374 return parent.getParent(); 375 } 376 377 /** 378 * Returns the site path 379 * @return the site path 380 */ 381 public String getSitePath () 382 { 383 String path = getName(); 384 385 Site parentSite = getParentSite(); 386 while (parentSite != null) 387 { 388 path = parentSite.getName() + "/" + path; 389 parentSite = parentSite.getParentSite(); 390 } 391 392 return path; 393 } 394 395 /** 396 * Returns the {@link RestrictedPagePolicy} associated with this Site. 397 * @return the {@link RestrictedPagePolicy} associated with this Site. 398 */ 399 public RestrictedPagePolicy getRestrictedPagePolicy() 400 { 401 Boolean displayRestrictedPages = getValue(__DISPLAY_RESTRICTED_PAGES_PARAMETER, false, true); 402 403 if (displayRestrictedPages) 404 { 405 return RestrictedPagePolicy.DISPLAYED; 406 } 407 else 408 { 409 return RestrictedPagePolicy.HIDDEN; 410 } 411 } 412 413 /** 414 * Returns the named {@link Site}. 415 * @param siteName the site name. 416 * @return the named {@link Site}. 417 * @throws UnknownAmetysObjectException if the named site does not exist. 418 */ 419 public Site getSite (String siteName) throws UnknownAmetysObjectException 420 { 421 DefaultTraversableAmetysObject root = getChild(SiteManager.ROOT_SITES); 422 return (Site) root.getChild(siteName); 423 } 424 425 /** 426 * Return true if the given site if an ancestor 427 * @param siteName the site name. 428 * @return true if the given site if an ancestor 429 */ 430 public boolean hasAncestor (String siteName) 431 { 432 // Is a parent ? 433 Site parentSite = getParentSite(); 434 while (parentSite != null) 435 { 436 if (parentSite.getName().equals(siteName)) 437 { 438 return true; 439 } 440 parentSite = parentSite.getParentSite(); 441 } 442 443 return false; 444 } 445 446 /** 447 * Return true if the given site if a descendant 448 * @param siteName the site name. 449 * @return true if the given site if a descendant 450 */ 451 public boolean hasDescendant (String siteName) 452 { 453 if (hasChildrenSite(siteName)) 454 { 455 return true; 456 } 457 458 AmetysObjectIterable<Site> childrenSites = getChildrenSites(); 459 for (Site child : childrenSites) 460 { 461 if (child.hasDescendant (siteName)) 462 { 463 return true; 464 } 465 } 466 467 return false; 468 } 469 470 /** 471 * Returns true if the given site exists. 472 * @param siteName the site name. 473 * @return true if the given site exists. 474 */ 475 public boolean hasChildrenSite(String siteName) 476 { 477 try 478 { 479 DefaultTraversableAmetysObject root = getChild(SiteManager.ROOT_SITES); 480 return root.hasChild(siteName); 481 } 482 catch (UnknownAmetysObjectException e) 483 { 484 return false; 485 } 486 } 487 488 /** 489 * Returns the sites names. 490 * @return the sites names. 491 */ 492 public Collection<String> getChildrenSiteNames() 493 { 494 AmetysObjectIterable<Site> sites = getChildrenSites(); 495 496 ArrayList<String> result = new ArrayList<>(); 497 498 for (Site site : sites) 499 { 500 result.add(site.getName()); 501 } 502 503 return result; 504 } 505 506 /** 507 * Returns all children sites, or empty if none. 508 * @return all children sites, or empty if none. 509 * @throws AmetysRepositoryException if an error occurs 510 */ 511 public AmetysObjectIterable<Site> getChildrenSites() throws AmetysRepositoryException 512 { 513 try 514 { 515 TraversableAmetysObject rootSites = getChild(SiteManager.ROOT_SITES); 516 return rootSites.getChildren(); 517 } 518 catch (UnknownAmetysObjectException e) 519 { 520 return new CollectionIterable<>(new ArrayList<>()); 521 } 522 } 523 524 /** 525 * Returns the illustration of the site 526 * @return the illustration of the site 527 */ 528 public Binary getIllustration() 529 { 530 return getValue(ILLUSTRATION_PARAMETER); 531 } 532 533 /** 534 * Generates SAX events for the site's illustration 535 * @param contentHandler the {@link ContentHandler} that will receive the SAX events 536 * @throws SAXException if error occurs during the SAX events generation 537 * @throws IOException if an I/O error occurs while reading the illustration 538 */ 539 public void illustrationToSAX(ContentHandler contentHandler) throws SAXException, IOException 540 { 541 dataToSAX(contentHandler, ILLUSTRATION_PARAMETER); 542 } 543 544 /** 545 * Set the illustration of the site 546 * @param is The input stream of the illustration 547 * @param mimeType The mime type of the illustration 548 * @param filename The filename of the illustration 549 * @param lastModificationDate The last modification date of the illustration 550 * @throws IOException if an error occurs while setting the illustration 551 */ 552 public void setIllustration(InputStream is, String mimeType, String filename, ZonedDateTime lastModificationDate) throws IOException 553 { 554 if (is != null) 555 { 556 Binary illustration = new Binary(); 557 illustration.setInputStream(is); 558 Optional.ofNullable(mimeType).ifPresent(illustration::setMimeType); 559 Optional.ofNullable(filename).ifPresent(illustration::setFilename); 560 Optional.ofNullable(lastModificationDate).ifPresent(illustration::setLastModificationDate); 561 setValue(ILLUSTRATION_PARAMETER, illustration); 562 } 563 else 564 { 565 removeValue(ILLUSTRATION_PARAMETER); 566 } 567 } 568 569 @Override 570 public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException 571 { 572 // First remove subsites 573 for (Site site : getChildrenSites()) 574 { 575 site.remove(); 576 } 577 578 // Then remove the contents of the site, for shared web contents, copy of the 579 // original content to the target site will be done. 580 for (Content content : getContents()) 581 { 582 // FIXME API check if not modifiable 583 if (content instanceof ModifiableContent) 584 { 585 // CMS-10661 Remove zone items of the web content first because if the content 586 // is shared, there are some saving operations in the middle of the copy 587 // operation (workflow is initialized and there is a save directly on the 588 // session. 589 if (content instanceof WebContent) 590 { 591 for (ZoneItem zoneItem : ((WebContent) content).getReferencingZoneItems()) 592 { 593 if (zoneItem instanceof ModifiableZoneItem) 594 { 595 ((ModifiableZoneItem) zoneItem).remove(); 596 } 597 } 598 } 599 600 ((ModifiableContent) content).remove(); 601 } 602 } 603 604 super.remove(); 605 } 606 607 @Override 608 public boolean canMoveTo(AmetysObject newParent) throws AmetysRepositoryException 609 { 610 return newParent instanceof Site || newParent instanceof AmetysObjectCollection; 611 } 612 613 @Override 614 public void moveTo(AmetysObject newParent, boolean renameIfExist) throws AmetysRepositoryException, RepositoryIntegrityViolationException 615 { 616 Node node = getNode(); 617 618 try 619 { 620 if (getParent().equals(newParent)) 621 { 622 // Do nothing 623 } 624 else 625 { 626 if (!canMoveTo(newParent)) 627 { 628 throw new AmetysRepositoryException("Site " + toString() + " can only be moved to a site or the root of site"); 629 } 630 631 String name = node.getName(); 632 633 try 634 { 635 // Move node 636 if (newParent instanceof Site) 637 { 638 Site parentSite = (Site) newParent; 639 if (!parentSite.hasChild("ametys-internal:sites")) 640 { 641 parentSite.createChild("ametys-internal:sites", "ametys:sites"); 642 } 643 node.getSession().move(node.getPath(), parentSite.getNode().getPath() + "/ametys-internal:sites/" + name); 644 } 645 else 646 { 647 Node contextNode = NodeHelper.getOrCreateFinalHashNode(((AmetysObjectCollection) newParent).getNode(), name); 648 node.getSession().move(node.getPath(), contextNode.getPath() + "/" + name); 649 } 650 651 } 652 catch (ItemExistsException e) 653 { 654 throw new AmetysRepositoryException(String.format("A site already exists for in parent path '%s'", newParent.getPath() + "/" + name), e); 655 } 656 657 // Invalidate parent path as the parent path has changed 658 _invalidateParentPath(); 659 } 660 } 661 catch (RepositoryException e) 662 { 663 throw new AmetysRepositoryException(String.format("Unable to move site '%s' to node '%s'", this, newParent.getId()), e); 664 } 665 } 666 667 @Override 668 public Site copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException 669 { 670 try 671 { 672 Node parentNode = _getParentNode(parent, name); 673 Node clonedSite = parentNode.addNode(name, RepositoryConstants.NAMESPACE_PREFIX + ":site"); 674 675 parent.saveChanges(); 676 677 Site site = (parent instanceof Site parentSite) 678 ? parentSite.getSite(name) 679 : parent.getChild(name); 680 681 // Copy properties 682 PropertyIterator properties = getNode().getProperties(RepositoryConstants.NAMESPACE_PREFIX + ":*"); 683 while (properties.hasNext()) 684 { 685 Property property = (Property) properties.next(); 686 String itemName = property.getName().substring((RepositoryConstants.NAMESPACE_PREFIX + ":").length()); 687 if (!hasDefinition(itemName)) 688 { 689 if (property.getDefinition().isMultiple()) 690 { 691 clonedSite.setProperty(property.getName(), property.getValues(), property.getType()); 692 } 693 else 694 { 695 clonedSite.setProperty(property.getName(), property.getValue(), property.getType()); 696 } 697 } 698 } 699 700 // Copy model 701 copyTo(site); 702 703 // Copy resources 704 Node resourcesNode = getNode().getNode("ametys-internal:resources"); 705 getNode().getSession().getWorkspace().copy(resourcesNode.getPath(), clonedSite.getPath() + "/" + "ametys-internal:resources"); 706 707 // Copy plugins (ametys-internal:plugins is auto-created) 708 NodeIterator plugins = getNode().getNode("ametys-internal:plugins").getNodes(); 709 while (plugins.hasNext()) 710 { 711 Node pluginNode = (Node) plugins.next(); 712 getNode().getSession().getWorkspace().copy(pluginNode.getPath(), clonedSite.getPath() + "/ametys-internal:plugins/" + pluginNode.getName()); 713 } 714 715 // Copy sitemaps (ametys-internal:sitemaps is auto-created) 716 NodeIterator sitemaps = getNode().getNode("ametys-internal:sitemaps").getNodes(); 717 while (sitemaps.hasNext()) 718 { 719 Node sitemapNode = (Node) sitemaps.next(); 720 getNode().getSession().getWorkspace().copy(sitemapNode.getPath(), clonedSite.getPath() + "/ametys-internal:sitemaps/" + sitemapNode.getName()); 721 } 722 723 // Copy contents (ametys-internal:contents is auto-created) 724 NodeIterator contents = getNode().getNode("ametys-internal:contents").getNodes(); 725 while (contents.hasNext()) 726 { 727 Node contentNode = (Node) contents.next(); 728 getNode().getSession().getWorkspace().copy(contentNode.getPath(), clonedSite.getPath() + "/ametys-internal:contents/" + contentNode.getName()); 729 } 730 731 return site; 732 } 733 catch (RepositoryException e) 734 { 735 throw new AmetysRepositoryException(e); 736 } 737 } 738 739 @Override 740 public AmetysObject copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException 741 { 742 return copyTo(parent, name); 743 } 744 745 private Node _getParentNode (ModifiableTraversableAmetysObject parent, String siteName) throws AmetysRepositoryException 746 { 747 try 748 { 749 if (parent instanceof Site) 750 { 751 return ((Site) parent).getNode().getNode("ametys-internal:sites"); 752 } 753 else if (parent instanceof SimpleAmetysObject) 754 { 755 return NodeHelper.getOrCreateFinalHashNode(((SimpleAmetysObject) parent).getNode(), siteName); 756 } 757 } 758 catch (RepositoryException e) 759 { 760 throw new AmetysRepositoryException("Unable to create child: " + siteName, e); 761 } 762 763 throw new AmetysRepositoryException("Unable to get JCR node of object : " + parent.getId()); 764 } 765 766 @Override 767 public void orderBefore(AmetysObject siblingNode) throws AmetysRepositoryException 768 { 769 throw new UnsupportedOperationException("Site ordering is not supported"); 770 } 771 772 public ModifiableIndexableDataHolder getDataHolder() 773 { 774 ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode()); 775 SiteType siteType = _getFactory().getSiteTypesExtensionPoint().getExtension(getType()); 776 if (siteType == null) 777 { 778 throw new IllegalStateException("Can't access data from site " + getSitePath() + " with unknown type '" + getType() + "'."); 779 } 780 return new DefaultModifiableModelAwareDataHolder(repositoryData, siteType); 781 } 782 783 public ActivityHolder getActivityHolder() throws RepositoryException 784 { 785 // The child node is provided by the activity-holder nodetype so we are sure it is there 786 return new DefaultActivityHolder(getChild(ACTIVITIES_ROOT_NODE_NAME), _getFactory()); 787 } 788}