001/*
002 *  Copyright 2015 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.ArrayList;
019import java.util.Collections;
020import java.util.Comparator;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import javax.jcr.Node;
028import javax.jcr.RepositoryException;
029import javax.jcr.Session;
030
031import org.apache.avalon.framework.parameters.Parameters;
032import org.apache.avalon.framework.service.ServiceException;
033import org.apache.avalon.framework.service.ServiceManager;
034import org.apache.cocoon.ProcessingException;
035import org.apache.cocoon.acting.ServiceableAction;
036import org.apache.cocoon.environment.ObjectModelHelper;
037import org.apache.cocoon.environment.Redirector;
038import org.apache.cocoon.environment.Request;
039import org.apache.cocoon.environment.SourceResolver;
040
041import org.ametys.cms.content.ContentHelper;
042import org.ametys.cms.repository.Content;
043import org.ametys.cms.repository.WorkflowAwareContent;
044import org.ametys.cms.workflow.history.ElementWithWorkflow;
045import org.ametys.cms.workflow.history.HistoryStep;
046import org.ametys.cms.workflow.history.VersionInformation;
047import org.ametys.cms.workflow.history.WorkflowHistory;
048import org.ametys.cms.workflow.history.WorkflowHistoryHelper;
049import org.ametys.core.cocoon.JSonReader;
050import org.ametys.plugins.repository.AmetysObjectResolver;
051import org.ametys.plugins.repository.version.VersionAwareAmetysObject;
052import org.ametys.plugins.workflow.store.AbstractJackrabbitWorkflowStore;
053import org.ametys.plugins.workflow.support.WorkflowProvider;
054import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
055
056/**
057 * This action returns the workflow history of a content
058 *
059 */
060public class ContentHistoryAction extends ServiceableAction
061{
062    /** The workflow provider */
063    protected WorkflowProvider _workflowProvider;
064
065    /** The ametys object resolver */
066    protected AmetysObjectResolver _resolver;
067
068    /** The content helper */
069    protected ContentHelper _contentHelper;
070
071    /** The workflow history helper */
072    protected WorkflowHistoryHelper _workflowHistoryHelper;
073    
074    @Override
075    public void service(ServiceManager serviceManager) throws ServiceException
076    {
077        super.service(serviceManager);
078        _workflowProvider = (WorkflowProvider) serviceManager.lookup(WorkflowProvider.ROLE);
079        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
080        _contentHelper = (ContentHelper) serviceManager.lookup(ContentHelper.ROLE);
081        _workflowHistoryHelper = (WorkflowHistoryHelper) serviceManager.lookup(WorkflowHistoryHelper.ROLE);
082    }
083
084    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
085    {
086        Request request = ObjectModelHelper.getRequest(objectModel);
087
088        String id = request.getParameter("contentId");
089        Content content = _resolver.resolveById(id);
090
091        assert content instanceof VersionAwareAmetysObject;
092
093        Map<String, Object> result = new HashMap<>();
094        List<Map<String, Object>> workflowSteps = new ArrayList<>();
095        
096        Set<String> validatedVersions = new HashSet<>();
097        
098        try
099        {
100            if (content instanceof WorkflowAwareContent)
101            {
102                WorkflowAwareContent workflowAwareContent = (WorkflowAwareContent) content;
103                
104                AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(workflowAwareContent);
105                long workflowId = workflowAwareContent.getWorkflowId();
106                int initialActionId = (int) _getInitialActionId(workflow, workflowAwareContent);
107
108                List<VersionInformation> versionsInformation = _resolveVersionInformations((VersionAwareAmetysObject) content);
109                ElementWithWorkflow element = new ElementWithWorkflow(
110                        _contentHelper.getTitle(content), 
111                        workflowAwareContent.getCreationDate(), 
112                        workflowAwareContent.getCreator(),
113                        versionsInformation
114                );
115                
116                WorkflowHistory workflowHistory = _workflowHistoryHelper.getWorkflowHistory(element, workflowId, workflow, initialActionId);
117                for (HistoryStep step : workflowHistory.getSteps())
118                {
119                    workflowSteps.add(_workflowHistoryHelper.historyStep2Json(step));
120                }
121                validatedVersions = workflowHistory.getValidatedVersions();
122            }
123        }
124        catch (RepositoryException e)
125        {
126            throw new ProcessingException("Unable to access version history", e);
127        }
128        
129        for (Map<String, Object> stepInfo : workflowSteps)
130        {
131            @SuppressWarnings("unchecked")
132            List<Map<String, Object>> versions = (List<Map<String, Object>>) stepInfo.get("versions");
133            
134            for (Map<String, Object> version : versions)
135            {
136                String versionName = (String) version.get("name");
137                if (validatedVersions.contains(versionName))
138                {
139                    version.put("valid", true);
140                }
141            }
142        }
143
144        result.put("workflow", workflowSteps);
145        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
146        return EMPTY_MAP;
147    }
148
149    private long _getInitialActionId(AmetysObjectWorkflow workflow, WorkflowAwareContent waContent)
150    {
151        try
152        {
153            Session session = waContent.getNode().getSession();
154            AbstractJackrabbitWorkflowStore workflowStore = (AbstractJackrabbitWorkflowStore) workflow.getConfiguration().getWorkflowStore();
155            Node workflowEntryNode = workflowStore.getEntryNode(session, waContent.getWorkflowId());
156            return workflowEntryNode.getProperty("ametys-internal:initialActionId").getLong();
157        }
158        catch (Exception e)
159        {
160            getLogger().error("Unable to retrieves initial action id for workflow aware content : " + waContent.getId(), e);
161            return 0;
162        }
163    }
164
165    private List<VersionInformation> _resolveVersionInformations(VersionAwareAmetysObject content) throws RepositoryException
166    {
167        List<VersionInformation> versionsInformation = new ArrayList<>();
168
169        for (String revision : content.getAllRevisions())
170        {
171            VersionInformation versionInformation = new VersionInformation(revision, content.getRevisionTimestamp(revision));
172
173            for (String label : content.getLabels(revision))
174            {
175                versionInformation.addLabel(label);
176            }
177
178            versionsInformation.add(versionInformation);
179        }
180
181        // Sort by date descendant
182        Collections.sort(versionsInformation, new Comparator<VersionInformation>()
183        {
184            public int compare(VersionInformation o1, VersionInformation o2)
185            {
186                try
187                {
188                    return -o1.getCreatedAt().compareTo(o2.getCreatedAt());
189                }
190                catch (RepositoryException e)
191                {
192                    throw new RuntimeException("Unable to retrieve a creation date", e);
193                }
194            }
195        });
196
197        // Set the version name
198        int count = versionsInformation.size();
199        for (VersionInformation versionInformation : versionsInformation)
200        {
201            versionInformation.setVersionName(String.valueOf(count--));
202        }
203
204        return versionsInformation;
205    }
206}