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.SKIP_BUILTIN_CHECK)
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.SKIP_BUILTIN_CHECK)
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.SKIP_BUILTIN_CHECK)
136    public Set<String> getPropertiesNames(String workflowName, Integer stepId, Integer actionId)
137    {
138        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
139        _workflowRightHelper.checkEditRight(workflowDescriptor);
140        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
141        return properties.keySet();
142    }
143
144    /**
145     * Add a property to a workflow element
146     * @param workflowName unique name of current workflow
147     * @param stepId id of current step
148     * @param actionId id of current action, can be null if only a step is selected
149     * @param name the new name to save
150     * @param value the value to save
151     * @return the property's infos or an error message
152     */
153    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
154    public Map<String, Object> addProperty(String workflowName, Integer stepId, Integer actionId, String name, String value)
155    {
156        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
157        _workflowRightHelper.checkEditRight(workflowDescriptor);
158        if (StringUtils.isBlank(name))
159        {
160            return Map.of("message", "blank-name");
161        }
162        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
163        if (properties.get(name) != null)
164        {
165            return Map.of("message", "duplicate-name");
166        }
167        properties.put(name, value);
168        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
169        
170        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, name, value, true);
171    }
172
173    /**
174     * Edit property in a workflow element
175     * @param workflowName unique name of current workflow
176     * @param stepId id of current step
177     * @param actionId id of current action, can be null if only a step is selected
178     * @param oldName the old name for this property
179     * @param newName the new name to save
180     * @param newValue the value to save
181     * @return the property's infos or an error message
182     */
183    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
184    public Map<String, Object> editProperty(String workflowName, Integer stepId, Integer actionId, String oldName, String newName, String newValue)
185    {
186        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
187        _workflowRightHelper.checkEditRight(workflowDescriptor);
188        if (StringUtils.isBlank(newName))
189        {
190            return Map.of("message", "blank-name");
191        }
192        
193        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
194        
195        if (StringUtils.isNotBlank(oldName) && !oldName.equals(newName))
196        {
197            properties.remove(oldName);
198        }
199        properties.put(newName, newValue);
200        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
201        
202        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, newName, newValue, true);
203    }
204    
205    /**
206     * Remove a property from a workflow element
207     * @param workflowName unique name of current workflow
208     * @param stepId id of current step
209     * @param actionId id of current action, can be null if only a step is selected
210     * @param name the new name to save
211     * @return the property's parent infos or an error message
212     */
213    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
214    public Map<String, Object> deleteProperty(String workflowName, Integer stepId, Integer actionId, String name)
215    {
216        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
217        _workflowRightHelper.checkEditRight(workflowDescriptor);
218        if (StringUtils.isBlank(name))
219        {
220            return Map.of("message", "blank-name");
221        }
222        
223        Map<String, Object> properties = _getProperties(workflowDescriptor, stepId, actionId);
224        properties.remove(name);
225        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
226        
227        return _getPropertiesInfos(workflowDescriptor, stepId, actionId, name, null, true);
228    }
229    
230    private Map<String, Object> _getProperties(WorkflowDescriptor workflowDescriptor, Integer stepId, Integer actionId)
231    {
232        Map<String, Object> metaAttributes = new HashMap<>();
233        
234        if (actionId == null)
235        {
236            StepDescriptor step = workflowDescriptor.getStep(stepId);
237            metaAttributes = step.getMetaAttributes();
238        }
239        else
240        {
241            ActionDescriptor action = workflowDescriptor.getAction(actionId);
242            metaAttributes = action.getMetaAttributes();
243        }
244        
245        return metaAttributes;
246    }
247    
248    private Map<String, Object> _getPropertiesInfos(WorkflowDescriptor workflow, Integer stepId, Integer actionId, String name, String value, boolean hasChanges)
249    {
250        Map<String, Object> results = new HashMap<>();
251        results.put("workflowId", workflow.getName());
252        results.put("hasChanges", hasChanges);
253        results.put("stepId", stepId);
254        results.put("stepLabel", _workflowStepDAO.getStepLabel(workflow, stepId));
255        if (actionId != null)
256        {
257            ActionDescriptor action = workflow.getAction(actionId);
258            results.put("actionId", actionId);
259            results.put("actionLabel", _workflowTransitionDAO.getActionLabel(action));
260        }
261        results.put("name", name);
262        results.put("value", value);
263        return results;
264    }
265    
266}