001/*
002 *  Copyright 2014 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.HashMap;
020import java.util.Map;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.context.Context;
024import org.apache.avalon.framework.context.ContextException;
025import org.apache.avalon.framework.context.Contextualizable;
026import org.apache.avalon.framework.logger.AbstractLogEnabled;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.cocoon.components.ContextHelper;
031import org.apache.cocoon.environment.ObjectModelHelper;
032import org.apache.cocoon.environment.Request;
033
034import org.ametys.cms.repository.Content;
035import org.ametys.cms.repository.WorkflowAwareContent;
036import org.ametys.core.user.CurrentUserProvider;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.plugins.repository.AmetysRepositoryException;
039import org.ametys.plugins.workflow.AbstractWorkflowComponent;
040import org.ametys.plugins.workflow.support.WorkflowProvider;
041import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow;
042
043import com.opensymphony.workflow.InvalidActionException;
044import com.opensymphony.workflow.WorkflowException;
045 
046/**
047 * A component to do workflow actions on Content
048 */
049public class ContentWorkflowHelper extends AbstractLogEnabled implements Serviceable, Contextualizable, Component
050{
051    /** The component role */
052    public static final String ROLE = ContentWorkflowHelper.class.getName();
053    
054    /** Component to get the current user */
055    protected CurrentUserProvider _userProvider;
056    
057    /** Workflow instance. */
058    protected WorkflowProvider _workflowProvider;
059
060    private Context _context;
061
062
063    @Override
064    public void contextualize(Context context) throws ContextException
065    {
066        _context = context;
067    }
068    
069    @Override
070    public void service(ServiceManager manager) throws ServiceException
071    {
072        _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
073        _workflowProvider = (WorkflowProvider) manager.lookup(WorkflowProvider.ROLE);
074    }
075    
076    /**
077     * Creates a content using the workflow (with the CreateContentFunction).
078     * @param workflowName The name of the workflow to create
079     * @param initialActionId The workflow action id that creates content
080     * @param contentName The new name
081     * @param contentTitle The new title
082     * @param contentTypes The new content types. Cannot be null. Cannot be empty.
083     * @param mixins The new mixins. Can be null. Can be empty.
084     * @param languageCode The language code of the new content (such as 'fr', 'en'...)
085     * @return The workflow result map. See the create content function used to get the new content. Can be under the key CreateContentFunction.CONTENT_KEY, and the id under the key "contentId"
086     * @throws WorkflowException If an error occurred while doing the action on the workflow
087     * @throws AmetysRepositoryException If cannot get the workflow identifier of the content
088     */
089    public Map<String, Object> createContent(String workflowName, int initialActionId, String contentName, String contentTitle, String[] contentTypes, String[] mixins, String languageCode) throws AmetysRepositoryException, WorkflowException
090    {
091        return createContent(workflowName, initialActionId, contentName, contentTitle, contentTypes, mixins, languageCode, null, null);
092    }
093
094    /**
095     * Creates a content using the workflow (with the CreateContentFunction).
096     * @param workflowName The name of the workflow to create
097     * @param initialActionId The workflow action id that creates content
098     * @param contentName The new name
099     * @param contentTitle The new title
100     * @param contentTypes The new content types. Cannot be null. Cannot be empty.
101     * @param mixins The new mixins. Can be null. Can be empty.
102     * @param languageCode The language code of the new content (such as 'fr', 'en'...)
103     * @param parentContentId If the new content is a subcontent, the parent content identifier.
104     * @param parentContentMetadatapath If the new content is a subcontent, the path of the metadata where the new content will take place in its parent
105     * @return The workflow result map. See the create content function used to get the new content. Can be under the key CreateContentFunction.CONTENT_KEY, and the id under the key "contentId"
106     * @throws WorkflowException If an error occurred while doing the action on the workflow
107     * @throws AmetysRepositoryException If cannot get the workflow identifier of the content
108
109     */
110    public Map<String, Object> createContent(String workflowName, int initialActionId, String contentName, String contentTitle, String[] contentTypes, String[] mixins, String languageCode, String parentContentId, String parentContentMetadatapath) throws AmetysRepositoryException, WorkflowException
111    {
112        Map<String, Object> inputs = new HashMap<>();
113        return createContent(workflowName, initialActionId, contentName, contentTitle, contentTypes, mixins, languageCode, parentContentId, parentContentMetadatapath, inputs);
114    }
115    
116    /**
117     * Creates a content using the workflow (with the CreateContentFunction).
118     * @param workflowName The name of the workflow to create
119     * @param initialActionId The workflow action id that creates content
120     * @param contentName The new name
121     * @param contentTitle The new title
122     * @param contentTypes The new content types. Cannot be null. Cannot be empty.
123     * @param mixins The new mixins. Can be null. Can be empty.
124     * @param languageCode The language code of the new content (such as 'fr', 'en'...)
125     * @param parentContentId If the new content is a subcontent, the parent content identifier.
126     * @param parentContentMetadatapath If the new content is a subcontent, the path of the metadata where the new content will take place in its parent
127     * @param inputs The parameters to transmit to the workflow functions. Cannot be null. 
128     * @return The workflow result map. See the create content function used to get the new content. Can be under the key CreateContentFunction.CONTENT_KEY, and the id under the key "contentId"
129     * @throws WorkflowException If an error occurred while doing the action on the workflow
130     * @throws AmetysRepositoryException If cannot get the workflow identifier of the content
131
132     */
133    public Map<String, Object> createContent(String workflowName, int initialActionId, String contentName, String contentTitle, String[] contentTypes, String[] mixins, String languageCode, String parentContentId, String parentContentMetadatapath, Map<String, Object> inputs) throws AmetysRepositoryException, WorkflowException 
134    {
135        inputs.put(CreateContentFunction.CONTENT_NAME_KEY, contentName);
136        inputs.put(CreateContentFunction.CONTENT_TITLE_KEY, contentTitle);
137        inputs.put(CreateContentFunction.CONTENT_TYPES_KEY, contentTypes);
138        inputs.put(CreateContentFunction.CONTENT_MIXINS_KEY, mixins);
139        inputs.put(CreateContentFunction.CONTENT_LANGUAGE_KEY, languageCode);
140        inputs.put(CreateContentFunction.PARENT_CONTENT_ID_KEY, parentContentId);
141        inputs.put(CreateContentFunction.PARENT_CONTENT_METADATA_PATH_KEY, parentContentMetadatapath);
142        
143        Map<String, Object> results = new HashMap<>();
144        inputs.put(AbstractWorkflowComponent.RESULT_MAP_KEY, results);
145        inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String>());
146        
147        try
148        {
149            AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow();
150            workflow.initialize(workflowName, initialActionId, inputs);
151        }
152        catch (WorkflowException e)
153        {
154            getLogger().error("An error occured while creating workflow '" + workflowName + "' with action '" + initialActionId + "' to creates content '" + contentName + "'", e);
155            throw e;
156        }
157        
158        return results;
159    }
160    
161    /**
162     * Get the available workflow actions for the content
163     * @param content The content to consider. Cannot be null.
164     * @return The array of actions ids that are available now
165     */
166    public int[] getAvailableActions(WorkflowAwareContent content)
167    {
168        Map<String, Object> inputs = new HashMap<>();
169        return getAvailableActions(content, inputs);
170    }
171    
172    /**
173     * Get the available workflow actions for the content
174     * @param content The content to consider. Cannot be null.
175     * @param inputs The parameters to transmit to the workflow functions. Cannot be null. 
176     * @return The array of actions ids that are available now
177     */
178    public int[] getAvailableActions(WorkflowAwareContent content, Map<String, Object> inputs)
179    {
180        AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content);
181        long wId = content.getWorkflowId();
182        
183        inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, content);
184        inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String> ());
185        
186        return workflow.getAvailableActions(wId, inputs);
187    }
188    
189    /**
190     * Do a workflow action on a content.
191     * @param content The content to act on. Cannot be null.
192     * @param actionId The id of the workflow action to do
193     * @return The results of the functions
194     * @throws WorkflowException If an error occurred while doing the action on the workflow
195     * @throws AmetysRepositoryException If cannot get the workflow identifier of the content
196     */
197    public Map<String, Object> doAction(WorkflowAwareContent content, int actionId) throws AmetysRepositoryException, WorkflowException
198    {
199        Map<String, Object> inputs = new HashMap<>();
200        return doAction(content, actionId, inputs);
201    }
202    
203    /**
204     * Do a workflow action on a content.
205     * @param content The content to act on. Cannot be null.
206     * @param actionId The id of the workflow action to do
207     * @param inputs The parameters to transmit to the workflow functions. Cannot be null. The special key AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY will be filled with the parent context if null (this means that if your are in a request dispatched, you will automatically get the js parameters).
208     * @return The results of the functions
209     * @throws WorkflowException If an error occurred while doing the action on the workflow
210     * @throws AmetysRepositoryException If cannot get the workflow identifier of the content
211     */
212    public Map<String, Object> doAction(WorkflowAwareContent content, int actionId, Map<String, Object> inputs) throws AmetysRepositoryException, WorkflowException
213    {
214        if (getLogger().isInfoEnabled())
215        {
216            getLogger().info("User " + _getUser() + " try to perform action " + actionId + " on content " + content.getId());
217        }
218
219        Map<String, Object> results = new HashMap<>();
220        inputs.put(AbstractWorkflowComponent.RESULT_MAP_KEY, results);
221        inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, content);
222        inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String>());
223        
224        if (inputs.get(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY) == null)
225        {
226            Map objectModel = ContextHelper.getObjectModel(_context);
227            @SuppressWarnings("unchecked")
228            Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
229            inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, jsParameters);
230        }
231
232        try
233        {
234            Request request = ContextHelper.getRequest(_context);
235            request.setAttribute(Content.class.getName(), content);
236            
237            AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(content);
238            workflow.doAction(content.getWorkflowId(), actionId, inputs);
239        }
240        catch (InvalidActionException e)
241        {
242            getLogger().error("An error occured while do workflow action '" + actionId + "' on content '" + content.getId() + "'", e);
243            throw e; 
244        }
245        
246        return results;
247    }
248
249    private UserIdentity _getUser()
250    {
251        return _userProvider.getUser();
252    }
253}