001/* 002 * Copyright 2011 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.page; 017 018import java.io.InputStream; 019import java.io.OutputStream; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Optional; 026import java.util.Properties; 027import java.util.Set; 028import java.util.stream.Stream; 029 030import javax.jcr.Node; 031import javax.jcr.RepositoryException; 032import javax.xml.transform.OutputKeys; 033import javax.xml.transform.TransformerFactory; 034import javax.xml.transform.sax.SAXTransformerFactory; 035import javax.xml.transform.sax.TransformerHandler; 036import javax.xml.transform.stream.StreamResult; 037 038import org.apache.avalon.framework.component.Component; 039import org.apache.avalon.framework.logger.AbstractLogEnabled; 040import org.apache.avalon.framework.service.ServiceException; 041import org.apache.avalon.framework.service.ServiceManager; 042import org.apache.avalon.framework.service.Serviceable; 043import org.apache.commons.lang3.StringUtils; 044import org.apache.excalibur.xml.sax.SAXParser; 045import org.apache.xml.serializer.OutputPropertiesFactory; 046import org.xml.sax.ContentHandler; 047import org.xml.sax.InputSource; 048 049import org.ametys.cms.content.references.OutgoingReferences; 050import org.ametys.cms.content.references.OutgoingReferencesExtractor; 051import org.ametys.cms.contenttype.ContentType; 052import org.ametys.cms.contenttype.RichTextUpdater; 053import org.ametys.cms.data.ContentDataHelper; 054import org.ametys.cms.data.ExplorerFile; 055import org.ametys.cms.data.File; 056import org.ametys.cms.data.RichText; 057import org.ametys.cms.repository.Content; 058import org.ametys.cms.repository.ModifiableContent; 059import org.ametys.cms.repository.WorkflowAwareContent; 060import org.ametys.cms.repository.WorkflowAwareContentHelper; 061import org.ametys.plugins.repository.AmetysObject; 062import org.ametys.plugins.repository.AmetysObjectIterable; 063import org.ametys.plugins.repository.AmetysObjectResolver; 064import org.ametys.plugins.repository.AmetysRepositoryException; 065import org.ametys.plugins.repository.ModifiableAmetysObject; 066import org.ametys.plugins.repository.RepositoryConstants; 067import org.ametys.plugins.repository.TraversableAmetysObject; 068import org.ametys.plugins.repository.UnknownAmetysObjectException; 069import org.ametys.plugins.repository.data.UnknownDataException; 070import org.ametys.plugins.repository.data.holder.DataHolder; 071import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 072import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 073import org.ametys.plugins.repository.data.holder.ModifiableDataHolder; 074import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 075import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder; 076import org.ametys.plugins.repository.data.holder.group.Repeater; 077import org.ametys.plugins.repository.data.holder.group.RepeaterEntry; 078import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper; 079import org.ametys.plugins.repository.data.type.ModelItemTypeConstants; 080import org.ametys.plugins.repository.version.VersionableAmetysObject; 081import org.ametys.plugins.workflow.support.WorkflowProvider; 082import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 083import org.ametys.runtime.model.ElementDefinition; 084import org.ametys.runtime.model.ModelItem; 085import org.ametys.runtime.model.exception.BadItemTypeException; 086import org.ametys.runtime.model.exception.NotUniqueTypeException; 087import org.ametys.runtime.model.exception.UndefinedItemPathException; 088import org.ametys.runtime.model.exception.UnknownTypeException; 089import org.ametys.web.repository.ModifiableSiteAwareAmetysObject; 090import org.ametys.web.repository.content.WebContent; 091import org.ametys.web.repository.page.ZoneItem.ZoneType; 092import org.ametys.web.repository.site.Site; 093import org.ametys.web.repository.sitemap.Sitemap; 094import org.ametys.web.site.CopyUpdaterExtensionPoint; 095 096import com.opensymphony.workflow.spi.Step; 097 098/** 099 * Component for copying site or pages 100 * 101 */ 102public class CopySiteComponent extends AbstractLogEnabled implements Component, Serviceable 103{ 104 /** Avalon Role */ 105 public static final String ROLE = CopySiteComponent.class.getName(); 106 107 /** The service manager. */ 108 protected ServiceManager _manager; 109 110 private AmetysObjectResolver _resolver; 111 private WorkflowProvider _workflowProvider; 112 113 private CopyUpdaterExtensionPoint _updaterEP; 114 private OutgoingReferencesExtractor _outgoingReferencesExtractor; 115 116 @Override 117 public void service(ServiceManager serviceManager) throws ServiceException 118 { 119 _manager = serviceManager; 120 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 121 _workflowProvider = (WorkflowProvider) serviceManager.lookup(WorkflowProvider.ROLE); 122 _updaterEP = (CopyUpdaterExtensionPoint) serviceManager.lookup(CopyUpdaterExtensionPoint.ROLE); 123 _outgoingReferencesExtractor = (OutgoingReferencesExtractor) serviceManager.lookup(OutgoingReferencesExtractor.ROLE); 124 } 125 126 /** 127 * This methods must be used after calling <code>copyTo</code> on a Page. 128 * Its updates references to ametys objects for metadata of new created pages and contents 129 * @param originalPage the original page 130 * @param createdPage the created page after a copy 131 * @throws AmetysRepositoryException if an error occurs 132 */ 133 public void updateReferencesAfterCopy (Page originalPage, Page createdPage) throws AmetysRepositoryException 134 { 135 // Update references to ametys object on metadata 136 _updateReferencesToAmetysObjects (createdPage, originalPage, createdPage); 137 138 for (Zone zone : createdPage.getZones()) 139 { 140 _updateReferencesToAmetysObjects (zone, originalPage, createdPage); 141 142 try (AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems()) 143 { 144 for (ZoneItem zoneItem : zoneItems) 145 { 146 _updateReferencesToAmetysObjects (zoneItem, originalPage, createdPage); 147 148 if (zoneItem.getType().equals(ZoneType.SERVICE)) 149 { 150 _updateReferencesToAmetysObjects (zoneItem, originalPage, createdPage); 151 } 152 else 153 { 154 Content content = zoneItem.getContent(); 155 _updateReferencesToAmetysObjects(content, originalPage, createdPage); 156 } 157 } 158 } 159 } 160 161 // Browse child pages 162 for (Page childPage : createdPage.getChildrenPages()) 163 { 164 updateReferencesAfterCopy ((Page) originalPage.getChild(childPage.getName()), childPage); 165 } 166 } 167 168 /** 169 * This method must be used after calling <code>copyTo</code> on a Site. 170 * Its updates contents and pages after a site copy 171 * @param originalSite the original site 172 * @param createdSite the created site after copy 173 * @throws AmetysRepositoryException if an error occurs 174 */ 175 public void updateSiteAfterCopy (Site originalSite, Site createdSite) throws AmetysRepositoryException 176 { 177 updateContentsAfterCopy (originalSite, createdSite); 178 updatePagesAfterCopy (originalSite, createdSite); 179 180 Set<String> ids = _updaterEP.getExtensionsIds(); 181 for (String id : ids) 182 { 183 _updaterEP.getExtension(id).updateSite(originalSite, createdSite); 184 } 185 } 186 187 /** 188 * This method re-initializes workflow, updates the site name for web content and updates references to ametys objects on metadata after a site copy 189 * @param initialSite the original site 190 * @param createdSite the created site after copy 191 * @throws AmetysRepositoryException if an error occurs 192 */ 193 public void updateContentsAfterCopy (Site initialSite, Site createdSite) throws AmetysRepositoryException 194 { 195 AmetysObjectIterable<Content> contents = createdSite.getContents(); 196 for (Content content : contents) 197 { 198 String relPath = content.getPath().substring(createdSite.getPath().length() + 1); 199 WebContent initialContent = initialSite.getChild(relPath); 200 201 try 202 { 203 // Re-init workflow 204 if (content instanceof WorkflowAwareContent) 205 { 206 _reinitWorkflow ((WorkflowAwareContent) content); 207 } 208 209 // Update site name 210 if (content instanceof ModifiableSiteAwareAmetysObject) 211 { 212 ((ModifiableSiteAwareAmetysObject) content).setSiteName(createdSite.getName()); 213 } 214 215 if (content instanceof ModifiableContent) 216 { 217 // Update references to ametys object on attributes 218 _updateReferencesToAmetysObjects(content, initialSite, createdSite); 219 220 // Update links in RichText 221 updateLinksInRichText (initialSite, createdSite, initialContent, content); 222 } 223 224 // Updaters 225 Set<String> ids = _updaterEP.getExtensionsIds(); 226 for (String id : ids) 227 { 228 _updaterEP.getExtension(id).updateContent(initialSite, createdSite, initialContent, content); 229 } 230 231 // save 232 if (content instanceof ModifiableAmetysObject) 233 { 234 ((ModifiableAmetysObject) content).saveChanges(); 235 } 236 237 // Creates the first version 238 if (content instanceof VersionableAmetysObject) 239 { 240 ((VersionableAmetysObject) content).checkpoint(); 241 } 242 } 243 catch (Exception e) 244 { 245 // Do not make the copy fail. 246 getLogger().warn("[Site copy] An error occured while updating content '" + content.getId() + " after copy from initial content '" + initialContent.getId() + "'", e); 247 } 248 } 249 250 if (createdSite.needsSave()) 251 { 252 createdSite.saveChanges(); 253 } 254 } 255 256 /** 257 * Updates references all references in a content to another one. 258 * @param initialContent the initial content. 259 * @param destContent the destination content. 260 */ 261 public void updateSharedContent(WebContent initialContent, WebContent destContent) 262 { 263 updateSharedContent(initialContent, destContent, true); 264 } 265 266 /** 267 * Updates references all references in a content to another one. 268 * @param initialContent the initial content. 269 * @param destContent the destination content. 270 * @param reinitWorkflow set to 'true' to reinitialize the workflow 271 */ 272 public void updateSharedContent(WebContent initialContent, WebContent destContent, boolean reinitWorkflow) 273 { 274 Site initialSite = initialContent.getSite(); 275 Site createdSite = destContent.getSite(); 276 277 // Re-init workflow 278 if (reinitWorkflow && destContent instanceof WorkflowAwareContent) 279 { 280 _reinitWorkflow((WorkflowAwareContent) destContent); 281 } 282 283 // Update references to ametys object on attributes 284 _updateReferencesToAmetysObjects(destContent, initialContent, destContent); 285 286 // Update links in RichText 287 updateLinksInRichText(initialContent, destContent, initialContent, destContent); 288 289 // Updaters 290 Set<String> ids = _updaterEP.getExtensionsIds(); 291 for (String id : ids) 292 { 293 _updaterEP.getExtension(id).updateContent(initialSite, createdSite, initialContent, destContent); 294 } 295 296 // save 297 if (destContent instanceof ModifiableAmetysObject) 298 { 299 ((ModifiableAmetysObject) destContent).saveChanges(); 300 } 301 302 // Creates the first version 303 if (destContent instanceof VersionableAmetysObject) 304 { 305 ((VersionableAmetysObject) destContent).checkpoint(); 306 } 307 } 308 309 /** 310 * This method analyzes content rich texts and update links if necessary 311 * @param initialAO The initial object copied 312 * @param createdAO The target object 313 * @param initialContent The initial content 314 * @param createdContent The created content after copy to update 315 * @throws AmetysRepositoryException if an error occurs 316 */ 317 public void updateLinksInRichText (TraversableAmetysObject initialAO, TraversableAmetysObject createdAO, Content initialContent, Content createdContent) throws AmetysRepositoryException 318 { 319 SAXParser saxParser = null; 320 try 321 { 322 if (createdContent instanceof ModifiableContent) 323 { 324 saxParser = (SAXParser) _manager.lookup(SAXParser.ROLE); 325 326 Map<String, Object> params = new HashMap<>(); 327 params.put("initialContent", initialContent); 328 params.put("createdContent", createdContent); 329 params.put("initialAO", initialAO); 330 params.put("createdAO", createdAO); 331 332 Map<String, Object> richTexts = DataHolderHelper.findEditableItemsByType(createdContent, org.ametys.cms.data.type.ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID); 333 for (Map.Entry<String, Object> entry : richTexts.entrySet()) 334 { 335 Object value = entry.getValue(); 336 if (value != null) 337 { 338 String attributePath = entry.getKey(); 339 ModelItem attributeDefinition = createdContent.getDefinition(attributePath); 340 ContentType contentType = (ContentType) attributeDefinition.getModel(); 341 RichTextUpdater richTextUpdater = contentType.getRichTextUpdater(); 342 343 if (value instanceof RichText) 344 { 345 _updateRichText((RichText) value, richTextUpdater, params, saxParser); 346 } 347 else if (value instanceof RichText[]) 348 { 349 for (RichText richText : (RichText[]) value) 350 { 351 _updateRichText(richText, richTextUpdater, params, saxParser); 352 } 353 } 354 355 ((ModifiableContent) createdContent).setValue(attributePath, value); 356 } 357 } 358 359 // Outgoing references 360 Map<String, OutgoingReferences> outgoingReferencesByPath = _outgoingReferencesExtractor.getOutgoingReferences(createdContent); 361 ((ModifiableContent) createdContent).setOutgoingReferences(outgoingReferencesByPath); 362 } 363 } 364 catch (Exception e) 365 { 366 // Do not failed the copy 367 getLogger().warn("An error occured while updating links in RichText for content '" + createdContent.getId() + " after copy from initial content '" + initialContent.getId() + "'", e); 368 } 369 finally 370 { 371 _manager.release(saxParser); 372 } 373 } 374 375 private void _updateRichText(RichText richText, RichTextUpdater richTextUpdater, Map<String, Object> params, SAXParser saxParser) throws Exception 376 { 377 try (InputStream is = richText.getInputStream(); OutputStream os = richText.getOutputStream()) 378 { 379 // create a transformer for saving sax into a file 380 TransformerHandler th = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler(); 381 382 // create the result where to write 383 StreamResult result = new StreamResult(os); 384 th.setResult(result); 385 386 // create the format of result 387 Properties format = new Properties(); 388 format.put(OutputKeys.METHOD, "xml"); 389 format.put(OutputKeys.INDENT, "yes"); 390 format.put(OutputKeys.ENCODING, "UTF-8"); 391 format.put(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "2"); 392 th.getTransformer().setOutputProperties(format); 393 394 ContentHandler richTextHandler = richTextUpdater.getContentHandler(th, th, params); 395 saxParser.parse(new InputSource(is), richTextHandler); 396 } 397 } 398 399 /** 400 * This method updates the site name of pages and updates references to ametys objects on page's metadata after a site copy 401 * @param originalSite the original site 402 * @param createdSite the created site after copy 403 * @throws AmetysRepositoryException if an error occurs 404 */ 405 public void updatePagesAfterCopy (Site originalSite, Site createdSite) throws AmetysRepositoryException 406 { 407 AmetysObjectIterable<Sitemap> sitemaps = createdSite.getSitemaps(); 408 409 for (Sitemap sitemap : sitemaps) 410 { 411 for (Page page : sitemap.getChildrenPages()) 412 { 413 _updatePageAfterCopy (originalSite, createdSite, page); 414 } 415 } 416 } 417 418 private void _updatePageAfterCopy (Site originalSite, Site createdSite, Page page) throws AmetysRepositoryException 419 { 420 try 421 { 422 // Update site name 423 if (page instanceof ModifiablePage) 424 { 425 ((ModifiablePage) page).setSiteName(createdSite.getName()); 426 } 427 428 // Update references to ametys object on metadata 429 _updateReferencesToAmetysObjects (page, originalSite, createdSite); 430 431 for (Zone zone : page.getZones()) 432 { 433 _updateReferencesToAmetysObjects (zone, originalSite, createdSite); 434 435 try (AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems()) 436 { 437 for (ZoneItem zoneItem : zoneItems) 438 { 439 try 440 { 441 _updateZoneItemAfterCopy(originalSite, createdSite, zoneItem); 442 } 443 catch (Exception e) 444 { 445 getLogger().warn("An error occured while updating zone item '" + zoneItem.getId() + "' (" + zoneItem.getPath() + ") after copy", e); 446 } 447 } 448 } 449 } 450 } 451 catch (AmetysRepositoryException e) 452 { 453 // Do not failed the copy 454 getLogger().warn("An error occured while updating page '" + page.getId() + "' (" + page.getPathInSitemap() + ") after copy", e); 455 } 456 457 // Browse child pages 458 for (Page childPage : page.getChildrenPages()) 459 { 460 _updatePageAfterCopy (originalSite, createdSite, childPage); 461 } 462 463 // Updaters 464 Set<String> ids = _updaterEP.getExtensionsIds(); 465 for (String id : ids) 466 { 467 _updaterEP.getExtension(id).updatePage(originalSite, createdSite, page); 468 } 469 } 470 471 private void _updateZoneItemAfterCopy(Site originalSite, Site createdSite, ZoneItem zoneItem) 472 { 473 _updateReferencesToAmetysObjects (zoneItem, originalSite, createdSite); 474 475 if (zoneItem.getType().equals(ZoneType.SERVICE)) 476 { 477 _updateReferencesToAmetysObjects (zoneItem.getServiceParameters(), originalSite, createdSite); 478 } 479 else if (zoneItem.getType().equals(ZoneType.CONTENT) && zoneItem instanceof ModifiableZoneItem) 480 { 481 Content content = zoneItem.getContent(); 482 String path = content.getPath(); 483 484 String originalPath = originalSite.getPath(); 485 if (path.startsWith(originalPath)) 486 { 487 String relPath = path.substring(originalPath.length() + 1); 488 try 489 { 490 // Find symmetric object on copied sub-tree 491 Content child = createdSite.getChild(relPath); 492 ((ModifiableZoneItem) zoneItem).setContent(child); 493 } 494 catch (UnknownAmetysObjectException e) 495 { 496 // Nothing 497 } 498 } 499 } 500 } 501 502 503 504 private void _updateReferencesToAmetysObjects (DataHolder dataHolder, TraversableAmetysObject originalAO, TraversableAmetysObject createdAO) 505 { 506 try 507 { 508 for (String dataName : dataHolder.getDataNames()) 509 { 510 if (ModelItemTypeConstants.COMPOSITE_TYPE_ID.equals(_getDataTypeID(dataHolder, dataName))) 511 { 512 _updateReferencesToAmetysObjects(dataHolder.getComposite(dataName), originalAO, createdAO); 513 } 514 else if (ModelItemTypeConstants.REPEATER_TYPE_ID.equals(_getDataTypeID(dataHolder, dataName)) && dataHolder instanceof ModelAwareDataHolder) 515 { 516 // Repeaters are only available on model aware data holders 517 Repeater repeater = ((ModelAwareDataHolder) dataHolder).getRepeater(dataName); 518 for (RepeaterEntry entry : repeater.getEntries()) 519 { 520 _updateReferencesToAmetysObjects(entry, originalAO, createdAO); 521 } 522 } 523 else if (_isAmetysObject(dataHolder, dataName)) 524 { 525 _updateReferenceToAmetysObject(dataHolder, dataName, originalAO, createdAO); 526 } 527 } 528 } 529 catch (UndefinedItemPathException | UnknownDataException | UnknownTypeException | NotUniqueTypeException e) 530 { 531 // The type of the data has not been determined so there is no way to determine if it is a reference to an ametys object 532 } 533 534 } 535 536 private void _updateReferenceToAmetysObject (DataHolder dataHolder, String dataName, TraversableAmetysObject originalAO, TraversableAmetysObject createdAO) throws AmetysRepositoryException 537 { 538 boolean isDataModifiable = dataHolder instanceof ModifiableDataHolder; 539 if (isDataModifiable && dataHolder instanceof ModelAwareDataHolder modelAwareDataHolder) 540 { 541 ElementDefinition definition = (ElementDefinition) modelAwareDataHolder.getDefinition(dataName); // definition can be casted hare. No way we can have groups here. 542 isDataModifiable = definition.isEditable(); 543 } 544 545 if (isDataModifiable) 546 { 547 if (_isDataMultiple(dataHolder, dataName)) 548 { 549 String[] ids = _getValue(dataHolder, dataName); 550 List<String> newReferences = new ArrayList<>(); 551 boolean hasNewReference = false; 552 for (String id : ids) 553 { 554 String newReference = _getNewReferenceToAmetysObject(originalAO, createdAO, id).orElse(id); 555 newReferences.add(newReference); 556 hasNewReference |= !newReference.equals(id); 557 } 558 559 if (hasNewReference) 560 { 561 _setValue((ModifiableDataHolder) dataHolder, dataName, newReferences.toArray(new String[newReferences.size()])); 562 } 563 } 564 else 565 { 566 String id = _getValue(dataHolder, dataName); 567 _getNewReferenceToAmetysObject(originalAO, createdAO, id) 568 .ifPresent(newReference -> _setValue((ModifiableDataHolder) dataHolder, dataName, newReference)); 569 } 570 } 571 } 572 573 /** 574 * Retrieves the updated reference to an ametys object. 575 * If the object has been copied into the created site, the new reference is the symmetric object on created site 576 * Otherwise, the reference does not change, there is no new reference to retrieve 577 * @param originalAO the original ametys object containing the reference 578 * @param createdAO the created ametys object where to update the reference 579 * @param id the id of the referenced ametys object to potentially update 580 * @return the new reference to the ametys object, an empty {@link Optional} if the reference does not to change 581 */ 582 private Optional<String> _getNewReferenceToAmetysObject(TraversableAmetysObject originalAO, TraversableAmetysObject createdAO, String id) 583 { 584 AmetysObject ametysObject = _resolver.resolveById(id); 585 String path = ametysObject.getPath(); 586 587 String originalPath = originalAO.getPath(); 588 if (path.startsWith(originalPath + "/")) 589 { 590 String relPath = path.substring(originalPath.length() + 1); 591 try 592 { 593 // Find symmetric object on copied sub-tree 594 AmetysObject child = createdAO.getChild(relPath); 595 return Optional.of(child.getId()); 596 } 597 catch (UnknownAmetysObjectException e) 598 { 599 getLogger().warn("Object of path " + relPath + " was not found on copied sub-tree " + createdAO.getPath(), e); 600 return Optional.empty(); 601 } 602 catch (AmetysRepositoryException e) 603 { 604 getLogger().error("Unable to retrieve object of path " + relPath + " on copied sub-tree " + createdAO.getPath(), e); 605 return Optional.empty(); 606 } 607 } 608 else 609 { 610 return Optional.empty(); 611 } 612 } 613 614 private boolean _isAmetysObject (DataHolder dataHolder, String dataName) 615 { 616 try 617 { 618 String typeId = _getDataTypeID(dataHolder, dataName); 619 switch (typeId) 620 { 621 case org.ametys.cms.data.type.ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID: 622 return dataHolder.hasValue(dataName); 623 case org.ametys.runtime.model.type.ModelItemTypeConstants.STRING_TYPE_ID: 624 case org.ametys.cms.data.type.ModelItemTypeConstants.FILE_ELEMENT_TYPE_ID: 625 if (dataHolder.hasValue(dataName)) 626 { 627 if (_isDataMultiple(dataHolder, dataName)) 628 { 629 String[] values = _getValue(dataHolder, dataName); 630 for (String value : values) 631 { 632 if (_resolver.hasAmetysObjectForId(value)) 633 { 634 return true; 635 } 636 } 637 638 // None of the value of the multiple data is a reference 639 return false; 640 } 641 else 642 { 643 String value = _getValue(dataHolder, dataName); 644 return _resolver.hasAmetysObjectForId(value); 645 } 646 } 647 else 648 { 649 return false; 650 } 651 default: 652 return false; 653 } 654 } 655 catch (AmetysRepositoryException | UndefinedItemPathException | UnknownTypeException | NotUniqueTypeException e) 656 { 657 return false; 658 } 659 } 660 661 private String _getDataTypeID(DataHolder dataHolder, String dataName) throws UndefinedItemPathException, UnknownDataException, UnknownTypeException, NotUniqueTypeException 662 { 663 return dataHolder instanceof ModelAwareDataHolder ? ((ModelAwareDataHolder) dataHolder).getType(dataName).getId() : ((ModelLessDataHolder) dataHolder).getType(dataName).getId(); 664 } 665 666 private boolean _isDataMultiple(DataHolder dataHolder, String dataName) 667 { 668 return dataHolder instanceof ModelAwareDataHolder ? ((ModelAwareDataHolder) dataHolder).isMultiple(dataName) : ((ModelLessDataHolder) dataHolder).isMultiple(dataName); 669 } 670 671 @SuppressWarnings("unchecked") 672 private <T> T _getValue(DataHolder dataHolder, String dataName) 673 { 674 if (dataHolder instanceof ModelAwareDataHolder) 675 { 676 ModelAwareDataHolder modelAwareDataHolder = (ModelAwareDataHolder) dataHolder; 677 String typeId = _getDataTypeID(dataHolder, dataName); 678 if (org.ametys.cms.data.type.ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(typeId)) 679 { 680 return _isDataMultiple(dataHolder, dataName) 681 ? (T) ContentDataHelper.getContentIdsArrayFromMultipleContentData(modelAwareDataHolder, dataName) 682 : (T) ContentDataHelper.getContentIdFromContentData(modelAwareDataHolder, dataName); 683 } 684 else if (org.ametys.cms.data.type.ModelItemTypeConstants.FILE_ELEMENT_TYPE_ID.equals(typeId)) 685 { 686 return _isDataMultiple(dataHolder, dataName) 687 ? (T) _getResourceIdsArrayFromMultipleFileData(modelAwareDataHolder, dataName) 688 : (T) _getResourceIdFromFileData(modelAwareDataHolder, dataName); 689 } 690 else 691 { 692 return modelAwareDataHolder.getValue(dataName); 693 } 694 } 695 else 696 { 697 return ((ModelLessDataHolder) dataHolder).getValue(dataName); 698 } 699 } 700 701 private String _getResourceIdFromFileData(ModelAwareDataHolder dataHolder, String dataPath) throws BadItemTypeException 702 { 703 File value = dataHolder.getValue(dataPath); 704 return Optional.ofNullable(value) 705 .filter(ExplorerFile.class::isInstance) 706 .map(ExplorerFile.class::cast) 707 .map(ExplorerFile::getResourceId) 708 .orElse(StringUtils.EMPTY); 709 } 710 711 private String[] _getResourceIdsArrayFromMultipleFileData(ModelAwareDataHolder dataHolder, String dataPath) throws BadItemTypeException 712 { 713 File[] value = dataHolder.getValue(dataPath); 714 return Optional.ofNullable(value) 715 .map(v -> Arrays.stream(v)) 716 .orElse(Stream.empty()) 717 .filter(ExplorerFile.class::isInstance) 718 .map(ExplorerFile.class::cast) 719 .map(ExplorerFile::getResourceId) 720 .toArray(String[]::new); 721 } 722 723 private void _setValue(ModifiableDataHolder dataHolder, String dataName, Object value) 724 { 725 if (dataHolder instanceof ModifiableModelAwareDataHolder) 726 { 727 ((ModifiableModelAwareDataHolder) dataHolder).setValue(dataName, value); 728 } 729 else 730 { 731 ((ModifiableModelLessDataHolder) dataHolder).setValue(dataName, value); 732 } 733 } 734 735 private void _reinitWorkflow (WorkflowAwareContent content) throws AmetysRepositoryException 736 { 737 try 738 { 739 long wId = content.getWorkflowId(); 740 741 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content); 742 String workflowName = workflow.getWorkflowName(wId); 743 744 // 1 - Delete the cloned workflow 745 WorkflowAwareContentHelper.removeWorkflowId(content); 746 747 // For legacy purpose, delete the workflow reference property if exists (only for contents created on 3.x versions) 748 Node node = content.getNode(); 749 try 750 { 751 if (node.hasProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":workflowRef")) 752 { 753 node.getProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":workflowRef").remove(); 754 } 755 } 756 catch (RepositoryException e) 757 { 758 throw new AmetysRepositoryException("Unable to remove workflowId property", e); 759 } 760 761 workflow.removeWorkflow(wId); 762 763 // 2 - Initialize new workflow instance 764 HashMap<String, Object> inputs = new HashMap<>(); 765 long workflowId = workflow.initialize(workflowName, 0, inputs); 766 content.setWorkflowId(workflowId); 767 768 // Update current step property 769 Step currentStep = (Step) workflow.getCurrentSteps(workflowId).iterator().next(); 770 content.setCurrentStepId(currentStep.getStepId()); 771 } 772 catch (Exception e) 773 { 774 throw new AmetysRepositoryException("Unable to initialize workflow for content " + content.getId(), e); 775 } 776 777 } 778}