001/*
002 *  Copyright 2023 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.plugins.workflow.dao;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023import java.util.Set;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.commons.lang3.StringUtils;
030
031import org.ametys.core.ui.Callable;
032import org.ametys.plugins.workflow.support.WorflowRightHelper;
033import org.ametys.plugins.workflow.support.WorkflowSessionHelper;
034import org.ametys.runtime.plugin.component.AbstractLogEnabled;
035
036import com.opensymphony.workflow.loader.ActionDescriptor;
037import com.opensymphony.workflow.loader.StepDescriptor;
038import com.opensymphony.workflow.loader.WorkflowDescriptor;
039
040/**
041 * DAO for workflow element's meta attributes
042 */
043public class WorkflowPropertyDAO extends AbstractLogEnabled implements Component, Serviceable
044{
045    /** The workflow session helper */
046    protected WorkflowSessionHelper _workflowSessionHelper;
047    
048    /** The workflow right helper */
049    protected WorflowRightHelper _workflowRightHelper;
050    
051    /** The workflow step DAO */
052    protected WorkflowStepDAO _workflowStepDAO;
053    
054    /** The workflow transition DAO */
055    protected WorkflowTransitionDAO _workflowTransitionDAO;
056    
057    public void service(ServiceManager smanager) throws ServiceException
058    {
059        _workflowSessionHelper = (WorkflowSessionHelper) smanager.lookup(WorkflowSessionHelper.ROLE);
060        _workflowRightHelper = (WorflowRightHelper) smanager.lookup(WorflowRightHelper.ROLE);
061        _workflowStepDAO = (WorkflowStepDAO) smanager.lookup(WorkflowStepDAO.ROLE);
062        _workflowTransitionDAO = (WorkflowTransitionDAO) smanager.lookup(WorkflowTransitionDAO.ROLE);
063    }
064    
065    /**
066     * Get properties of current step
067     * @param workflowName the workflow unique name
068     * @param stepId id of the current step
069     * @return a map of the properties
070     */
071    @SuppressWarnings("unchecked")
072    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
073    public Map<String, Object> getStepProperties(String workflowName, Integer stepId)
074    {
075        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
076        List<Map<String, Object>> properties2json = new ArrayList<>();
077        if (_workflowRightHelper.canRead(workflowDescriptor))
078        {
079            StepDescriptor step = workflowDescriptor.getStep(stepId);
080            if (step != null)
081            {
082                properties2json.addAll(_properties2JSON(step.getMetaAttributes()));
083            }
084        }
085        
086        return Map.of("data", properties2json);
087    }
088    
089    /**
090     * Get properties of current action
091     * @param workflowName the workflow unique name
092     * @param actionId id of the current action
093     * @return a map of the properties
094     */
095    @SuppressWarnings("unchecked")
096    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
097    public Map<String, Object> getActionProperties(String workflowName, Integer actionId)
098    {
099        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
100        List<Map<String, Object>> propertiesToJson = new ArrayList<>();
101        if (_workflowRightHelper.canRead(workflowDescriptor))
102        {
103            ActionDescriptor action = workflowDescriptor.getAction(actionId);
104            if (action != null)
105            {
106                propertiesToJson.addAll(_properties2JSON(action.getMetaAttributes()));
107            }
108        }
109        
110        return Map.of("data", propertiesToJson);
111    }
112    
113    private List<Map<String, Object>> _properties2JSON(Map<String, Object> metaAttributes)
114    {
115        List<Map<String, Object>> propertiesToJson = new ArrayList<>();
116        
117        for (Entry<String, Object> entry : metaAttributes.entrySet())
118        {
119            Map<String, Object> properties = new HashMap<>();
120            properties.put("id", entry.getKey());
121            properties.put("value", entry.getValue());
122            propertiesToJson.add(properties);
123        }
124        
125        return propertiesToJson;
126    }
127    
128    /**
129     * Get the used property names for current workflow element 
130     * @param workflowName unique name of current workflow
131     * @param stepId id of current step
132     * @param actionId id of current action, can be null if only a step is selected
133     * @return a set of the properties' names
134     */
135    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
136    public Set<String> getPropertiesNames(String workflowName, Integer stepId, Integer actionId)
137    {
138        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
139        
140        // Check user right
141        _workflowRightHelper.checkEditRight(workflowDescriptor);
142        
143        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
144        return properties.keySet();
145    }
146
147    /**
148     * Add a property to a workflow element
149     * @param workflowName unique name of current workflow
150     * @param stepId id of current step
151     * @param actionId id of current action, can be null if only a step is selected
152     * @param name the new name to save
153     * @param value the value to save
154     * @return the property's infos or an error message
155     */
156    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
157    public Map<String, Object> addProperty(String workflowName, Integer stepId, Integer actionId, String name, String value)
158    {
159        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
160        
161        // Check user right
162        _workflowRightHelper.checkEditRight(workflowDescriptor);
163        
164        if (StringUtils.isBlank(name))
165        {
166            return Map.of("message", "blank-name");
167        }
168        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
169        if (properties.get(name) != null)
170        {
171            return Map.of("message", "duplicate-name");
172        }
173        properties.put(name, value);
174        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
175        
176        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, name, value, true);
177    }
178
179    /**
180     * Edit property in a workflow element
181     * @param workflowName unique name of current workflow
182     * @param stepId id of current step
183     * @param actionId id of current action, can be null if only a step is selected
184     * @param oldName the old name for this property
185     * @param newName the new name to save
186     * @param newValue the value to save
187     * @return the property's infos or an error message
188     */
189    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
190    public Map<String, Object> editProperty(String workflowName, Integer stepId, Integer actionId, String oldName, String newName, String newValue)
191    {
192        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
193        
194        // Check user right
195        _workflowRightHelper.checkEditRight(workflowDescriptor);
196        
197        if (StringUtils.isBlank(newName))
198        {
199            return Map.of("message", "blank-name");
200        }
201        
202        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
203        
204        if (StringUtils.isNotBlank(oldName) && !oldName.equals(newName))
205        {
206            properties.remove(oldName);
207        }
208        properties.put(newName, newValue);
209        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
210        
211        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, newName, newValue, true);
212    }
213    
214    /**
215     * Remove a property from a workflow element
216     * @param workflowName unique name of current workflow
217     * @param stepId id of current step
218     * @param actionId id of current action, can be null if only a step is selected
219     * @param name the new name to save
220     * @return the property's parent infos or an error message
221     */
222    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
223    public Map<String, Object> deleteProperty(String workflowName, Integer stepId, Integer actionId, String name)
224    {
225        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
226        
227        // Check user right
228        _workflowRightHelper.checkEditRight(workflowDescriptor);
229        
230        if (StringUtils.isBlank(name))
231        {
232            return Map.of("message", "blank-name");
233        }
234        
235        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
236        properties.remove(name);
237        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
238        
239        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, name, null, true);
240    }
241    
242    private Map<String, Object> _getProperties(WorkflowDescriptor workflowDescriptor, Integer stepId, Integer actionId)
243    {
244        Map<String, Object> metaAttributes = new HashMap<>();
245        
246        if (actionId == null)
247        {
248            StepDescriptor step = workflowDescriptor.getStep(stepId);
249            metaAttributes = step.getMetaAttributes();
250        }
251        else
252        {
253            ActionDescriptor action = workflowDescriptor.getAction(actionId);
254            metaAttributes = action.getMetaAttributes();
255        }
256        
257        return metaAttributes;
258    }
259    
260    private Map<String, Object> _getPropertiesInfos(WorkflowDescriptor workflow, Integer stepId, Integer actionId, String name, String value, boolean hasChanges)
261    {
262        Map<String, Object> results = new HashMap<>();
263        results.put("workflowId", workflow.getName());
264        results.put("hasChanges", hasChanges);
265        results.put("stepId", stepId);
266        results.put("stepLabel", _workflowStepDAO.getStepLabel(workflow, stepId));
267        if (actionId != null)
268        {
269            ActionDescriptor action = workflow.getAction(actionId);
270            results.put("actionId", actionId);
271            results.put("actionLabel", _workflowTransitionDAO.getActionLabel(action));
272        }
273        results.put("name", name);
274        results.put("value", value);
275        return results;
276    }
277    
278}