001/* 002 * Copyright 2025 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.cms.workflow; 017 018import java.util.Map; 019 020import javax.jcr.Node; 021import javax.jcr.NodeIterator; 022import javax.jcr.RepositoryException; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.commons.collections.PredicateUtils; 027 028import org.ametys.cms.repository.CloneComponent; 029import org.ametys.cms.repository.Content; 030import org.ametys.cms.repository.WorkflowAwareContent; 031import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollection; 032import org.ametys.plugins.repository.AmetysObjectResolver; 033import org.ametys.plugins.repository.AmetysRepositoryException; 034import org.ametys.plugins.repository.version.VersionableAmetysObject; 035import org.ametys.runtime.i18n.I18nizableText; 036 037import com.opensymphony.module.propertyset.PropertySet; 038import com.opensymphony.workflow.WorkflowException; 039 040/** 041 * Restore the content of attachments from a previous version. 042 * This function is intended to be call after the {@link EditContentFunction} 043 * 044 * @implNote this implementation only support root of type {@link JCRResourcesCollection} 045 */ 046public class RestoreContentAttachmentsFunction extends AbstractContentFunction 047{ 048 private CloneComponent _cloneComponent; 049 private AmetysObjectResolver _resolver; 050 051 @Override 052 public void service(ServiceManager manager) throws ServiceException 053 { 054 super.service(manager); 055 _cloneComponent = (CloneComponent) manager.lookup(CloneComponent.ROLE); 056 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 057 } 058 059 public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException 060 { 061 Map<String, Object> resultsMap = getResultsMap(transientVars); 062 // Only restore the attachments if all went well with the edit 063 if (EditContentFunction.RESULT_STATE_OK.equals(resultsMap.get(EditContentFunction.RESULT_STATE_KEY))) 064 { 065 WorkflowAwareContent content = getContent(transientVars); 066 067 String contentVersion = (String) getContextParameters(transientVars).get("contentVersion"); 068 069 Content oldContent = _resolver.resolveById(content.getId()); 070 if (oldContent instanceof VersionableAmetysObject versionAware) 071 { 072 versionAware.switchToRevision(contentVersion); 073 } 074 075 if (content.getRootAttachments() instanceof JCRResourcesCollection currentRootAttachments) 076 { 077 try 078 { 079 boolean hasChanged = false; 080 if (oldContent.getRootAttachments() instanceof JCRResourcesCollection oldRootAttachments) 081 { 082 _cloneComponent.cloneNodeAndPreserveUUID( 083 oldRootAttachments.getNode(), 084 currentRootAttachments.getNode(), 085 PredicateUtils.truePredicate(), 086 PredicateUtils.truePredicate()); 087 hasChanged = currentRootAttachments.getNode().getSession().hasPendingChanges(); 088 } 089 // in the case where the old content don't have a root 090 // just delete the children of the current (but keep the root) 091 else if (oldContent.getRootAttachments() == null) 092 { 093 Node node = currentRootAttachments.getNode(); 094 095 NodeIterator subNodes = node.getNodes(); 096 while (subNodes.hasNext()) 097 { 098 hasChanged = true; 099 subNodes.nextNode().remove(); 100 } 101 } 102 else 103 { 104 _logger.warn("Failed to restore attachments on " + content.getId() + ". Restoring attachments from a root other than a JCRResourceCollection is not supported. The attachments were left untouched."); 105 } 106 107 if (hasChanged) 108 { 109 resultsMap.put(HAS_CHANGED_KEY, true); 110 } 111 } 112 catch (RepositoryException e) 113 { 114 throw new AmetysRepositoryException(e); 115 } 116 } 117 else if (content.getRootAttachments() == null 118 && oldContent.getRootAttachments() == null) 119 { 120 // if the current root is "empty" and the previous one was too. 121 // Do nothing. 122 } 123 else 124 { 125 _logger.warn("Failed to restore attachments on " + content.getId() + ". Restoring attachments on a root other than a JCRResourceCollection is not supported. The attachments were left untouched."); 126 } 127 } 128 } 129 130 public I18nizableText getLabel() 131 { 132 return new I18nizableText("plugin.cms", "PLUGINS_CMS_RESTORE_CONTENT_ATTACHMENTS_FUNCTION_LABEL"); 133 } 134}