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