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.cms.workflow;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import javax.jcr.NoSuchWorkspaceException;
022import javax.jcr.Node;
023import javax.jcr.Repository;
024import javax.jcr.RepositoryException;
025import javax.jcr.Session;
026
027import org.apache.avalon.framework.parameters.Parameters;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Redirector;
032import org.apache.cocoon.environment.Request;
033
034import org.ametys.cms.ObservationConstants;
035import org.ametys.cms.content.archive.ArchiveConstants;
036import org.ametys.cms.repository.CloneComponent;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.repository.WorkflowAwareContent;
039import org.ametys.core.cocoon.ActionResultGenerator;
040import org.ametys.core.observation.Event;
041import org.ametys.core.observation.ObservationManager;
042import org.ametys.core.user.UserIdentity;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.provider.AbstractRepository;
045import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
046
047import com.opensymphony.workflow.InvalidInputException;
048import com.opensymphony.workflow.WorkflowException;
049
050/**
051 * Action for archiving a content.
052 */
053public class ArchiveContentAction extends ContentWorkflowAction
054{
055    
056    /** The repository. */
057    protected Repository _repository;
058    
059    /** The ametys object resolver. */
060    protected AmetysObjectResolver _resolver;
061    
062    /** The clone component. */
063    protected CloneComponent _cloneComponent;
064    
065    /** Observation manager available to subclasses. */
066    protected ObservationManager _observationManager;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        super.service(serviceManager);
072        _repository = (Repository) serviceManager.lookup(AbstractRepository.ROLE);
073        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
074        _cloneComponent = (CloneComponent) serviceManager.lookup(CloneComponent.ROLE);
075        _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE);
076    }
077    
078    @Override
079    protected Map<String, String> _act(Redirector redirector, Map objectModel, String source, Parameters parameters, int actionId, Map inputs) throws InvalidInputException, WorkflowException
080    {
081        WorkflowAwareContent content = _getContent(objectModel);
082        
083        UserIdentity currentUser = _getUser(objectModel);
084        String contentId = content.getId();
085        
086        Event deletingEvent = _prepareDeletingEvent(currentUser, content);
087        Event deletedEvent = _prepareDeletedEvent(currentUser, content);
088        
089        try
090        {
091            // Trigger the action.
092            Map<String, String> result = super._act(redirector, objectModel, source, parameters, actionId, inputs);
093            
094            Map<String, Object> resultMap = new HashMap<>();
095            
096            _observationManager.notify(deletingEvent);
097            
098            // Move the content and its workflow to the "archives" workspace.
099            archiveContent(content, objectModel, resultMap);
100            
101            Request request = ObjectModelHelper.getRequest(objectModel);
102            request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, resultMap);
103            
104            // Notify that a content has been removed from the default workspace.
105            _observationManager.notify(deletedEvent);
106            
107            String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
108            try
109            {
110                // Resolve the content in the default workspace.
111                RequestAttributeWorkspaceSelector.setForcedWorkspace(request, ArchiveConstants.ARCHIVE_WORKSPACE);
112                WorkflowAwareContent archivedContent = _resolver.resolveById(contentId);
113                
114                if (archivedContent != null)
115                {
116                    // Notify that the archived content has been created from the archive workspace
117                    Map<String, Object> eventParams = new HashMap<>();
118                    eventParams.put(ObservationConstants.ARGS_CONTENT, archivedContent);
119                    eventParams.put(ObservationConstants.ARGS_CONTENT_NAME, archivedContent.getName());
120                    eventParams.put(ObservationConstants.ARGS_CONTENT_ID, archivedContent.getId());
121                    _observationManager.notify(new Event(org.ametys.cms.ObservationConstants.EVENT_CONTENT_ARCHIVED, currentUser, eventParams));
122                }
123            }
124            finally
125            {
126                // Restore context
127                RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
128            }
129            
130            return result;
131        }
132        catch (RepositoryException e)
133        {
134            throw new WorkflowException("Unable to archive the content " + contentId, e);
135        }
136    }
137    
138    /**
139     * Prepare event to notify observers
140     * @param issuer the responsible for the action
141     * @param content the archived content
142     * @return the event
143     */
144    protected Event _prepareDeletingEvent (UserIdentity issuer, Content content)
145    {
146        Map<String, Object> eventParams = new HashMap<>();
147        eventParams.put(ObservationConstants.ARGS_CONTENT, content);
148        eventParams.put(ObservationConstants.ARGS_CONTENT_NAME, content.getName());
149        eventParams.put(ObservationConstants.ARGS_CONTENT_ID, content.getId());
150        
151        return new Event(org.ametys.cms.ObservationConstants.EVENT_CONTENT_DELETING, issuer, eventParams);
152    }
153    
154    /**
155     * Prepare event to notify observers
156     * @param issuer the responsible for the action
157     * @param content the archived content
158     * @return the event
159     */
160    protected Event _prepareDeletedEvent (UserIdentity issuer, Content content)
161    {
162        Map<String, Object> eventParams = new HashMap<>();
163        eventParams.put(ObservationConstants.ARGS_CONTENT, content);
164        eventParams.put(ObservationConstants.ARGS_CONTENT_NAME, content.getName());
165        eventParams.put(ObservationConstants.ARGS_CONTENT_ID, content.getId());
166
167        return new Event(org.ametys.cms.ObservationConstants.EVENT_CONTENT_DELETED, issuer, eventParams);
168    }
169    
170    /**
171     * Archive a content.
172     * @param content the content to archive.
173     * @param objectModel the current object model.
174     * @param result the result map
175     * @throws RepositoryException The repository exception
176     */
177    protected void archiveContent(WorkflowAwareContent content, Map objectModel, Map<String, Object> result) throws RepositoryException
178    {
179        Session session = null;
180        Session archiveSession = null;
181        
182        try
183        {
184            archiveSession = getArchiveSession();
185            
186            Node contentNode = content.getNode();
187            
188            session = contentNode.getSession();
189            
190            _cloneComponent.cloneContentNodeWithWorkflow(archiveSession, contentNode);
191            
192            archiveSession.save();
193            
194            content.remove();
195            session.save();
196        }
197        finally
198        {
199            if (archiveSession != null)
200            {
201                archiveSession.logout();
202            }
203        }
204    }
205    
206    /**
207     * Get a repository session on the archive workspace.
208     * @return the archive session.
209     * @throws RepositoryException The repository exception
210     */
211    protected Session getArchiveSession() throws RepositoryException
212    {
213        Session session = null;
214        Session archiveSession = null;
215        
216        try
217        {
218            archiveSession = _repository.login(ArchiveConstants.ARCHIVE_WORKSPACE);
219            
220            return archiveSession;
221        }
222        catch (NoSuchWorkspaceException e)
223        {
224            session = _repository.login();
225            session.getWorkspace().createWorkspace(ArchiveConstants.ARCHIVE_WORKSPACE);
226            archiveSession = _repository.login(ArchiveConstants.ARCHIVE_WORKSPACE);
227
228            archiveSession.getRootNode().addNode(AmetysObjectResolver.ROOT_REPO, AmetysObjectResolver.ROOT_TYPE);
229            archiveSession.save();
230            
231            return archiveSession;
232        }
233        finally
234        {
235            if (session != null)
236            {
237                session.logout();
238            }
239        }
240    }
241    
242}