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.Collections;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Set;
025import java.util.stream.Collectors;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.cocoon.ProcessingException;
032import org.apache.commons.lang3.StringUtils;
033import org.apache.commons.lang3.tuple.Pair;
034
035import org.ametys.core.ui.Callable;
036import org.ametys.plugins.workflow.EnhancedFunction;
037import org.ametys.plugins.workflow.EnhancedFunction.FunctionType;
038import org.ametys.plugins.workflow.EnhancedFunctionExtensionPoint;
039import org.ametys.plugins.workflow.ModelItemTypeExtensionPoint;
040import org.ametys.plugins.workflow.component.WorkflowArgument;
041import org.ametys.plugins.workflow.support.AvalonTypeResolver;
042import org.ametys.plugins.workflow.support.WorflowRightHelper;
043import org.ametys.plugins.workflow.support.WorkflowElementDefinitionHelper;
044import org.ametys.plugins.workflow.support.WorkflowHelper.WorkflowVisibility;
045import org.ametys.plugins.workflow.support.WorkflowSessionHelper;
046import org.ametys.runtime.i18n.I18nizableText;
047import org.ametys.runtime.model.DefinitionContext;
048import org.ametys.runtime.model.ElementDefinition;
049import org.ametys.runtime.model.Model;
050import org.ametys.runtime.model.SimpleViewItemGroup;
051import org.ametys.runtime.model.StaticEnumerator;
052import org.ametys.runtime.model.View;
053import org.ametys.runtime.model.ViewElement;
054import org.ametys.runtime.model.disableconditions.DisableCondition.OPERATOR;
055import org.ametys.runtime.plugin.component.AbstractLogEnabled;
056
057import com.opensymphony.workflow.FunctionProvider;
058import com.opensymphony.workflow.TypeResolver;
059import com.opensymphony.workflow.WorkflowException;
060import com.opensymphony.workflow.loader.ActionDescriptor;
061import com.opensymphony.workflow.loader.DescriptorFactory;
062import com.opensymphony.workflow.loader.FunctionDescriptor;
063import com.opensymphony.workflow.loader.StepDescriptor;
064import com.opensymphony.workflow.loader.WorkflowDescriptor;
065
066/**
067 * DAO for workflow element's pre and pos functions
068 */
069public class WorkflowFunctionDAO extends AbstractLogEnabled implements Component, Serviceable
070{
071    /** Extension point for workflow arguments data type */
072    protected static ModelItemTypeExtensionPoint _workflowArgumentDataTypeExtensionPoint;
073    
074    private static final String __FUNCTION_DEFAULT_TYPE = "avalon";
075    private static final String __ATTRIBUTE_FUNCTIONS_LIST = "functions-list";
076    private static final String __ATTRIBUTE_FUNCTION_TYPES = "function-types";
077    
078    
079    /** The workflow session helper */
080    protected WorkflowSessionHelper _workflowSessionHelper;
081    
082    /** The workflow right helper */
083    protected WorflowRightHelper _workflowRightHelper;
084    
085    /** The workflow step DAO */
086    protected WorkflowStepDAO _workflowStepDAO;
087    
088    /** The workflow transition DAO */
089    protected WorkflowTransitionDAO _workflowTransitionDAO;
090    
091    /** The service manager */
092    protected ServiceManager _manager;
093
094    /** Extension point for EnhancedFunctions */
095    protected EnhancedFunctionExtensionPoint _enhancedFunctionExtensionPoint;
096    
097    public void service(ServiceManager smanager) throws ServiceException
098    {
099        _workflowSessionHelper = (WorkflowSessionHelper) smanager.lookup(WorkflowSessionHelper.ROLE);
100        _workflowRightHelper = (WorflowRightHelper) smanager.lookup(WorflowRightHelper.ROLE);
101        _workflowStepDAO = (WorkflowStepDAO) smanager.lookup(WorkflowStepDAO.ROLE);
102        _workflowTransitionDAO = (WorkflowTransitionDAO) smanager.lookup(WorkflowTransitionDAO.ROLE);
103        _enhancedFunctionExtensionPoint = (EnhancedFunctionExtensionPoint) smanager.lookup(EnhancedFunctionExtensionPoint.ROLE);
104        _workflowArgumentDataTypeExtensionPoint = (ModelItemTypeExtensionPoint) smanager.lookup(ModelItemTypeExtensionPoint.ROLE_WORKFLOW);
105        _manager = smanager;
106    }
107    
108    /**
109     * Get the function's parameters as fields to configure edition form panel
110     * @return the parameters field as Json readable map
111     * @throws ProcessingException exception while saxing view to json
112     */
113    @Callable(rights = {"Workflow_Right_Edit", "Workflow_Right_Edit_User"})
114    public Map<String, Object> getFunctionsModel() throws ProcessingException 
115    {
116        Map<String, Object> response = new HashMap<>();
117        
118        View view = new View();
119        SimpleViewItemGroup fieldset = new SimpleViewItemGroup();
120        fieldset.setName("functions");
121        
122        Set<Pair<String, EnhancedFunction>> enhancedFunctions = _enhancedFunctionExtensionPoint.getAllFunctions()
123                .stream()
124                .filter(this::_hasFunctionRight)
125                .collect(Collectors.toSet());
126        
127        List<ElementDefinition> modelItems = new ArrayList<>();
128        modelItems.add(_getFunctionListModelItem(enhancedFunctions));
129        modelItems.addAll(_getArgumentsAndTypeModelItems(enhancedFunctions));
130        Model.of("functions", "worklow", modelItems.toArray(new ElementDefinition[modelItems.size()]));
131        
132        for (ElementDefinition functionArgument : modelItems)
133        {
134            ViewElement argumentView = new ViewElement();
135            argumentView.setDefinition(functionArgument);
136            fieldset.addViewItem(argumentView);
137        }
138        
139        view.addViewItem(fieldset);
140        
141        response.put("parameters", view.toJSON(DefinitionContext.newInstance().withEdition(true)));
142        
143        return response;
144    }
145    
146    private boolean _hasFunctionRight(Pair<String, EnhancedFunction> function)
147    {
148        List<WorkflowVisibility> functionVisibilities = function.getRight().getVisibilities();
149        if (functionVisibilities.contains(WorkflowVisibility.USER))
150        {
151            return _workflowRightHelper.hasEditUserRight();
152        }
153        else if (functionVisibilities.contains(WorkflowVisibility.SYSTEM))
154        {
155            return _workflowRightHelper.hasEditSystemRight();
156        }
157        return false;
158    }
159
160    /**
161     * Get a list of workflow arguments model items with disable conditions on non related function selected
162     * @param enhancedFunctions a list of Pair with id and enhanced function
163     * @return the list of model items
164     */
165    protected List<ElementDefinition> _getArgumentsAndTypeModelItems(Set<Pair<String, EnhancedFunction>> enhancedFunctions)
166    {
167        List<ElementDefinition> argumentModelItems = new ArrayList<>();
168        for (Pair<String, EnhancedFunction> functionPair : enhancedFunctions)
169        {
170            String functionId = functionPair.getLeft();
171            
172            EnhancedFunction function = functionPair.getRight();
173            if (function.getFunctionExecType().equals(FunctionType.BOTH))
174            {
175                WorkflowArgument functionTypeArg = _getFunctionTypeModelItem(functionId);
176                WorkflowElementDefinitionHelper.addRelativeDisableCondition(functionTypeArg, __ATTRIBUTE_FUNCTIONS_LIST, OPERATOR.NEQ, functionId);
177                argumentModelItems.add(functionTypeArg);
178            }
179            
180            for (WorkflowArgument arg : function.getArguments())
181            {
182                arg.setName(functionId + "-" + arg.getName());
183                WorkflowElementDefinitionHelper.addRelativeDisableCondition(arg, __ATTRIBUTE_FUNCTIONS_LIST, OPERATOR.NEQ, functionId);
184                argumentModelItems.add(arg);
185            }
186        }
187        return argumentModelItems;
188    }
189    
190    /**
191     * Get the model item for the list of functions
192     * @param enhancedFunctions the list of enhanced functions
193     * @return an enum of the functions as a model item 
194     */
195    protected ElementDefinition<String> _getFunctionListModelItem(Set<Pair<String, EnhancedFunction>> enhancedFunctions)
196    {
197        ElementDefinition<String> functionsList = WorkflowElementDefinitionHelper.getElementDefinition(
198            __ATTRIBUTE_FUNCTIONS_LIST,
199            new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_ROLE"), 
200            new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_ROLE_DESC"),
201            true, 
202            false
203        );
204        StaticEnumerator<String> functionsStaticEnumerator = new StaticEnumerator<>();
205        for (Pair<String, EnhancedFunction> function : enhancedFunctions)
206        {
207            functionsStaticEnumerator.add(function.getRight().getLabel(), function.getLeft());
208        }
209        functionsList.setEnumerator(functionsStaticEnumerator);
210        
211        return functionsList;
212    }
213
214    /**
215     * Get the view item for the fonctions types
216     * @param functionId id of current function
217     * @return the view element
218     */
219    protected WorkflowArgument _getFunctionTypeModelItem(String functionId)
220    {
221        StaticEnumerator<String> functionTypeStaticEnum = new StaticEnumerator<>();
222        functionTypeStaticEnum.add(new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_PREFUNCTION_TYPE"), FunctionType.PRE.name());
223        functionTypeStaticEnum.add(new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_POSTFUNCTION_TYPE"), FunctionType.POST.name());
224        
225        WorkflowArgument<String> functionTypes = WorkflowElementDefinitionHelper.getElementDefinition(
226                __ATTRIBUTE_FUNCTION_TYPES + "-" + functionId,
227                new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_TYPES_LABEL"),
228                new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_ADD_FUNCTION_DIALOG_TYPES_DESC"),
229                true,
230                false
231            );
232        functionTypes.setEnumerator(functionTypeStaticEnum);
233        
234        return functionTypes;
235    }
236    
237    /**
238     * Get the function's parameters as a json view and their current values
239     * @param workflowName the workflow's unique name
240     * @param stepId the parent step's id
241     * @param actionId the parent action's id can be null
242     * @param type whether the function is a pre of post function
243     * @param id the function's id
244     * @param index the function's index in the pre/post function list
245     * @return map of the function's infos
246     */
247    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
248    public Map<String, Object> getFunctionParametersValues(String workflowName, Integer stepId, Integer actionId, String type, String id, Integer index) 
249    {
250        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
251        
252        // Check user right
253        _workflowRightHelper.checkEditRight(workflowDescriptor);
254        
255        Map<String, String> args = new HashMap<>();
256        
257        FunctionDescriptor function = _getFunction(workflowDescriptor, stepId, actionId, type, index);
258        
259        //change arguments key to make them unique
260        Map<String, String> arguments = function.getArgs();
261        for (Entry<String, String> entry : arguments.entrySet())
262        {
263            String argName = entry.getKey().equals("id") ? __ATTRIBUTE_FUNCTIONS_LIST : id + "-" + entry.getKey();
264            args.put(argName, entry.getValue());
265        }
266        
267        Map<String, Object> results = new HashMap<>();
268        results.put("parametersValues", args);
269        return results;
270    }
271
272    private FunctionDescriptor _getFunction(WorkflowDescriptor workflowDescriptor, Integer stepId, Integer actionId, String type, Integer index)
273    {
274        List<FunctionDescriptor> functions = _getTypedFunctions(workflowDescriptor, stepId, actionId, type);
275        FunctionDescriptor function = functions.get(index);
276        return function;
277    }
278
279    /**
280     * Add a function to the workflow element
281     * @param workflowName the workflow's unique name
282     * @param stepId the parent step's id
283     * @param actionId the parent action's id can be null
284     * @param params Map of the function arguments
285     * @return map of the function's infos
286     */
287    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
288    public Map<String, Object> addFunction(String workflowName, Integer stepId, Integer actionId, Map<String, Object> params)
289    {
290        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
291        
292        // Check user right
293        _workflowRightHelper.checkEditRight(workflowDescriptor);
294        
295        String functionId = (String) params.get(__ATTRIBUTE_FUNCTIONS_LIST);
296        EnhancedFunction enhancedFunction = _enhancedFunctionExtensionPoint.getExtension(functionId);
297        
298        String functionType = enhancedFunction.getFunctionExecType().equals(FunctionType.BOTH) 
299                ? (String) params.get(__ATTRIBUTE_FUNCTION_TYPES + "-" + functionId) 
300                : enhancedFunction.getFunctionExecType().name();
301        
302        List<FunctionDescriptor> functions = _getTypedFunctions(workflowDescriptor, stepId, actionId, functionType);
303        Map<String, String> functionParams = _getFunctionParamsValuesAsString(enhancedFunction, functionId, params);
304        FunctionDescriptor newFunction = _createFunctionDescriptor(functionParams);
305        functions.add(newFunction);
306        
307        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
308        
309        return _getFunctionInfos(workflowDescriptor, stepId, actionId, functionType, functionId, functions.size() - 1, true);
310    }
311
312    private FunctionDescriptor _createFunctionDescriptor(Map<String, String> functionParams)
313    {
314        DescriptorFactory factory = new DescriptorFactory();
315        FunctionDescriptor newFunction = factory.createFunctionDescriptor();
316        newFunction.setType(__FUNCTION_DEFAULT_TYPE);
317        newFunction.getArgs().putAll(functionParams);
318        
319        return newFunction;
320    }
321
322    /**
323     * Get the list of arguments as String, parse multiple arguments
324     * @param enhancedFunction the current function class
325     * @param functionId the function's id
326     * @param params List of function arguments with values
327     * @return the map of arguments formated for condition descriptor
328     */
329    protected Map<String, String> _getFunctionParamsValuesAsString(EnhancedFunction enhancedFunction, String functionId, Map<String, Object> params)
330    {
331        Map<String, String> functionParams = new HashMap<>();
332        functionParams.put("id", functionId);
333        List<WorkflowArgument> arguments = enhancedFunction.getArguments();
334        if (!arguments.isEmpty())
335        {
336            for (WorkflowArgument argument : arguments)
337            {
338                String paramKey = functionId + "-" + argument.getName();
339                String paramValue = argument.isMultiple()
340                        ?  _getListAsString(params, paramKey)
341                        : (String) params.get(paramKey);
342                if (StringUtils.isNotBlank(paramValue))
343                {
344                    functionParams.put(argument.getName(), paramValue);
345                }
346            }
347        }
348        return functionParams;
349    }
350
351    @SuppressWarnings("unchecked")
352    private String _getListAsString(Map<String, Object> params, String paramKey)
353    {
354        List<String> values = (List<String>) params.get(paramKey);
355        return values == null ? "" : String.join(",", values);
356    }
357
358    /**
359     * Get the list where belong a function can be either the prefunction list or the postfunctions
360     * @param workflowDescriptor the current workflow
361     * @param stepId id of current step
362     * @param actionId id of current action can be null if selection is a step
363     * @param functionType the type of function
364     * @return the typed list
365     */
366    protected List<FunctionDescriptor> _getTypedFunctions(WorkflowDescriptor workflowDescriptor, Integer stepId, Integer actionId, String functionType)
367    {
368        List<FunctionDescriptor> functions = new ArrayList<>();
369        if (actionId != null)
370        {
371            ActionDescriptor action = workflowDescriptor.getAction(actionId);
372            functions = functionType.equals(FunctionType.PRE.name()) ? action.getPreFunctions() : action.getPostFunctions();
373        }
374        else
375        {
376            StepDescriptor step = workflowDescriptor.getStep(stepId);
377            functions = functionType.equals(FunctionType.PRE.name()) ? step.getPreFunctions() : step.getPostFunctions();
378        }
379        return functions;
380    }
381    
382    /**
383     * Edit the function
384     * @param workflowName the workflow's unique name
385     * @param stepId the parent step's id
386     * @param actionId the parent action's id can be null
387     * @param oldType the saved type for this fonction
388     * @param params Map of the function arguments
389     * @param indexOfFunction the function's index in the pre/post function list
390     * @return map of the function's infos
391     */
392    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
393    public Map<String, Object> editFunction(String workflowName, Integer stepId, Integer actionId, String oldType, Map<String, Object> params, int indexOfFunction)
394    {
395        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
396        
397        // Check user right
398        _workflowRightHelper.checkEditRight(workflow);
399    
400        List<FunctionDescriptor> functions = _getTypedFunctions(workflow, stepId, actionId, oldType);
401        FunctionDescriptor function = functions.get(indexOfFunction);
402        
403        int index = indexOfFunction;
404        String functionId = (String) params.get(__ATTRIBUTE_FUNCTIONS_LIST);
405        EnhancedFunction enhancedFunction = _enhancedFunctionExtensionPoint.getExtension(functionId);
406        
407        String functionType = (String) params.get(__ATTRIBUTE_FUNCTION_TYPES + "-" + functionId);
408        String newType = StringUtils.isBlank(functionType) ? enhancedFunction.getFunctionExecType().name() : functionType;
409        
410        if (!oldType.equals(newType)) //Move function into the other typed list
411        {
412            functions.remove(function);
413            functions = _getTypedFunctions(workflow, stepId, actionId, newType);
414            functions.add(function);
415            index = functions.size() - 1;
416        }
417        
418        _updateFunctionArguments(function, functionId, enhancedFunction, params);
419        _workflowSessionHelper.updateWorkflowDescriptor(workflow);
420        
421        return _getFunctionInfos(workflow, stepId, actionId, newType, functionId, index, _isLast(functions, indexOfFunction));
422    }
423    
424    private void _updateFunctionArguments(FunctionDescriptor function, String functionId, EnhancedFunction enhancedFunction, Map<String, Object> params)
425    {
426        Map<String, String> args = function.getArgs();
427        args.clear();
428        args.putAll(_getFunctionParamsValuesAsString(enhancedFunction, functionId, params));
429    }
430    
431    /**
432     * Remove the function from its parent 
433     * @param workflowName the workflow's unique name
434     * @param stepId the parent step's id
435     * @param actionId the parent action's id can be null
436     * @param functionType whether the function is a pre of post function
437     * @param id the function's id
438     * @param indexToRemove the function's index in the pre/post function list
439     * @return map of the function's infos
440     */
441    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
442    public Map<String, Object> deleteFunction(String workflowName, Integer stepId, Integer actionId, String functionType, String id, int indexToRemove)
443    {
444        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
445        
446        // Check user right
447        _workflowRightHelper.checkEditRight(workflow);
448        
449        List<FunctionDescriptor> functions = _getTypedFunctions(workflow, stepId, actionId, functionType);
450        functions.remove(indexToRemove);
451        _workflowSessionHelper.updateWorkflowDescriptor(workflow);
452        
453        return _getFunctionInfos(workflow, stepId, actionId, functionType, id, indexToRemove, _isLast(functions, indexToRemove));
454    }
455    
456    /**
457     * Swap the function with the one before it in the parent's pre/post function list
458     * @param workflowName the workflow's unique name
459     * @param stepId the parent step's id
460     * @param actionId the parent action's id can be null
461     * @param functionType whether the function is a pre of post function
462     * @param functionIndex the function's index in the pre/post function list
463     * @return map of the function's infos
464     */
465    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
466    public Map<String, Object> moveUp(String workflowName, Integer stepId, Integer actionId, String functionType, int functionIndex)
467    {
468        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
469        
470        // Check user right
471        _workflowRightHelper.checkEditRight(workflow);
472        
473        if (functionIndex == 0)
474        {
475            return Map.of("message", "top-queue");
476        }
477        List<FunctionDescriptor> functions = _getTypedFunctions(workflow, stepId, actionId, functionType);
478        FunctionDescriptor functionToMove = functions.get(functionIndex);
479        Collections.swap(functions, functionIndex, functionIndex - 1);
480        _workflowSessionHelper.updateWorkflowDescriptor(workflow);
481        
482        return _getFunctionInfos(workflow, stepId, actionId, functionType, (String) functionToMove.getArgs().get("id"), functionIndex - 1, false);
483    }
484    
485    /**
486     * Swap the function with the one after it in the parent's pre/post function list
487     * @param workflowName the workflow's unique name
488     * @param stepId the parent step's id
489     * @param actionId the parent action's id can be null
490     * @param functionType whether the function is a pre of post function
491     * @param functionIndex the function's index in the pre/post function list
492     * @return map of the function's infos
493     */
494    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
495    public Map<String, Object> moveDown(String workflowName, Integer stepId, Integer actionId, String functionType, int functionIndex)
496    {
497        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
498        
499        // Check user right
500        _workflowRightHelper.checkEditRight(workflow);
501        
502        List<FunctionDescriptor> functions = _getTypedFunctions(workflow, stepId, actionId, functionType);
503        if (functionIndex == functions.size() - 1)
504        {
505            return Map.of("message", "bottom-queue");
506        }
507        FunctionDescriptor functionToMove = functions.get(functionIndex);
508        Collections.swap(functions, functionIndex, functionIndex + 1);
509        _workflowSessionHelper.updateWorkflowDescriptor(workflow);
510        
511        return _getFunctionInfos(workflow, stepId, actionId, functionType, (String) functionToMove.getArgs().get("id"), functionIndex + 1, _isLast(functions, functionIndex + 1));
512    }
513    
514    /**
515     * Check if function can have bigger index in its parent's pre/post function list
516     * @param functions the current list of functions
517     * @param functionIndex the function's index in the pre/post function list
518     * @return true if the function can move down
519     */
520    protected boolean _isLast(List<FunctionDescriptor> functions, int functionIndex)
521    {
522        return functionIndex == functions.size() - 1;
523    }
524    
525    /**
526     * Get pre and postfunctions of current step
527     * @param workflowName the workflow unique name
528     * @param stepId id of the current step
529     * @return a list of the step's functions
530     */
531    @SuppressWarnings("unchecked")
532    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
533    public Map<String, Object> getStepFunctions(String workflowName, Integer stepId)
534    {
535        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
536        List<Map<String, Object>> functions2json = new ArrayList<>();
537        if (_workflowRightHelper.canRead(workflowDescriptor))
538        {
539            StepDescriptor step = workflowDescriptor.getStep(stepId);
540            if (step != null)
541            {
542                functions2json.addAll(_functions2JSON(step.getPreFunctions(), FunctionType.PRE.name()));
543                functions2json.addAll(_functions2JSON(step.getPostFunctions(), FunctionType.POST.name()));
544            }
545        }
546        return Map.of("data", functions2json);
547    }
548    
549    /**
550     * Get pre and postfunctions of current action
551     * @param workflowName the workflow unique name
552     * @param actionId id of the current action
553     * @return a list of the action's functions
554     */
555    @SuppressWarnings("unchecked")
556    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
557    public Map<String, Object> getActionFunctions(String workflowName, Integer actionId)
558    {
559        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
560        List<Map<String, Object>> functions2json = new ArrayList<>();
561        if (_workflowRightHelper.canRead(workflowDescriptor))
562        {
563            ActionDescriptor action = workflowDescriptor.getAction(actionId);
564            if (action != null)
565            {
566                functions2json.addAll(_functions2JSON(action.getPreFunctions(), FunctionType.PRE.name()));
567                functions2json.addAll(_functions2JSON(action.getPostFunctions(), FunctionType.POST.name()));
568            }
569        }
570        
571        return Map.of("data", functions2json);
572    }
573    
574    private List<Map<String, Object>> _functions2JSON(List<FunctionDescriptor> functions, String type)
575    {
576        List<Map<String, Object>> functions2json = new ArrayList<>();
577        for (int index = 0; index < functions.size(); index++)
578        {
579            functions2json.add(_function2JSON(functions.get(index), type, index, _isLast(functions, index)));
580        }
581        return functions2json;
582    }
583    
584    private Map<String, Object> _function2JSON(FunctionDescriptor workflowFunction, String type, int index, boolean isLast)
585    {
586        String id = (String) workflowFunction.getArgs().get("id");
587        Map<String, Object> functionInfos = new HashMap<>();
588        functionInfos.put("type", type);
589        functionInfos.put("id", id);
590        functionInfos.put("index", index);
591        functionInfos.put("isLast", isLast);
592        
593        try
594        {
595            TypeResolver typeResolver = new AvalonTypeResolver(_manager);
596            FunctionProvider function = typeResolver.getFunction(workflowFunction.getType(), workflowFunction.getArgs());
597            if (function instanceof EnhancedFunction enhancedFunction)
598            {
599                I18nizableText description = _getFunctionDescription(workflowFunction, enhancedFunction);
600                if (description != null)
601                {
602                    functionInfos.put("description", description);
603                }
604            }
605        }
606        catch (WorkflowException e)
607        {
608            getLogger().error("Function " + id + " couldn't be resolved", e);
609        }
610        return functionInfos;
611    }
612
613    private I18nizableText _getFunctionDescription(FunctionDescriptor workflowFunction, EnhancedFunction enhancedFunction)
614    {
615        List<WorkflowArgument> arguments = enhancedFunction.getArguments();
616        Map<String, String> values = new HashMap<>();
617        for (WorkflowArgument arg : arguments)
618        {
619            values.put(arg.getName(), (String) workflowFunction.getArgs().get(arg.getName()));
620        }
621        
622        I18nizableText description = enhancedFunction.getFullLabel(values);
623        return description;
624    }
625    
626    /**
627     * Get the function's description
628     * @param workflowName the workflow's unique name
629     * @param stepId the parent step's id
630     * @param actionId the parent action's id can be null
631     * @param type whether the function is a pre of post function
632     * @param id the enhanced function's id
633     * @param index the function's index in the pre/post function list
634     * @return the function's description
635     */
636    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
637    public I18nizableText getFunctionDescription(String workflowName, Integer stepId, Integer actionId, String type, String id, Integer index)
638    {
639        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
640        
641        // Check user right
642        _workflowRightHelper.checkReadRight(workflow);
643        
644        FunctionDescriptor function = _getFunction(workflow, stepId, actionId, type, index);
645        EnhancedFunction enhancedFunction = _enhancedFunctionExtensionPoint.getExtension(id);
646        return _getFunctionDescription(function, enhancedFunction);
647    }
648    
649    private Map<String, Object> _getFunctionInfos(WorkflowDescriptor workflow, Integer stepId, Integer actionId, String type, String id, int index, boolean isLast)
650    {
651        Map<String, Object> results = new HashMap<>();
652        results.put("workflowId", workflow.getName());
653        results.put("stepId", stepId);
654        results.put("stepLabel", _workflowStepDAO.getStepLabel(workflow, stepId));
655        if (actionId != null)
656        {
657            ActionDescriptor action = workflow.getAction(actionId);
658            results.put("actionId", actionId);
659            results.put("actionLabel", _workflowTransitionDAO.getActionLabel(action));
660        }
661        results.put("type", type);
662        results.put("id", id);
663        results.put("index", index);
664        results.put("isLast", isLast);
665        return results;
666    }
667
668}