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.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.Set;
025import java.util.stream.Collectors;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032
033import org.ametys.core.ui.Callable;
034import org.ametys.core.util.I18nUtils;
035import org.ametys.core.util.StringUtils;
036import org.ametys.plugins.workflow.component.WorkflowLanguageManager;
037import org.ametys.plugins.workflow.support.I18nHelper;
038import org.ametys.plugins.workflow.support.WorflowRightHelper;
039import org.ametys.plugins.workflow.support.WorkflowHelper;
040import org.ametys.plugins.workflow.support.WorkflowSessionHelper;
041import org.ametys.runtime.i18n.I18nizableText;
042
043import com.opensymphony.workflow.loader.ActionDescriptor;
044import com.opensymphony.workflow.loader.DescriptorFactory;
045import com.opensymphony.workflow.loader.ResultDescriptor;
046import com.opensymphony.workflow.loader.StepDescriptor;
047import com.opensymphony.workflow.loader.WorkflowDescriptor;
048
049/**
050 * The workflow action DAO
051 */
052public class WorkflowTransitionDAO implements Component, Serviceable
053{
054    /** The component's role */
055    public static final String ROLE =  WorkflowTransitionDAO.class.getName();
056    
057    /** The default label for actions */
058    public static final I18nizableText DEFAULT_ACTION_NAME = new I18nizableText("plugin.workflow", "PLUGIN_WORKFLOW_DEFAULT_ACTION_LABEL");
059    
060    /** Default path for svg action icons */
061    private static final String __DEFAULT_SVG_ACTION_ICON_PATH = "plugin:cms://resources/img/history/workflow/action_0_16.png";
062    
063    /** Default path for node action icons */
064    private static final String __DEFAULT_ACTION_ICON_PATH = "/plugins/cms/resources/img/history/workflow/action_0_16.png";
065    
066    /** The workflow session helper */
067    protected WorkflowSessionHelper _workflowSessionHelper;
068    
069    /** The workflow right helper */
070    protected WorflowRightHelper _workflowRightHelper;
071    
072    /** The Workflow Language Manager */
073    protected WorkflowLanguageManager _workflowLanguageManager;
074    
075    /** The workflow helper */
076    protected WorkflowHelper _workflowHelper;
077    
078    /** The workflow step DAO */
079    protected WorkflowStepDAO _workflowStepDAO;
080    
081    /** The helper for i18n translations and catalogs */
082    protected I18nHelper _i18nHelper;
083    
084    /** The i18n utils */
085    protected I18nUtils _i18nUtils;
086    
087    /** The context */
088    protected Context _context;
089    
090    public void service(ServiceManager smanager) throws ServiceException
091    {
092        _workflowSessionHelper = (WorkflowSessionHelper) smanager.lookup(WorkflowSessionHelper.ROLE);
093        _workflowHelper = (WorkflowHelper) smanager.lookup(WorkflowHelper.ROLE);
094        _workflowRightHelper = (WorflowRightHelper) smanager.lookup(WorflowRightHelper.ROLE);
095        _i18nHelper = (I18nHelper) smanager.lookup(I18nHelper.ROLE);
096        _workflowLanguageManager = (WorkflowLanguageManager) smanager.lookup(WorkflowLanguageManager.ROLE);
097        _workflowStepDAO = (WorkflowStepDAO) smanager.lookup(WorkflowStepDAO.ROLE);
098        _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE);
099    }
100    
101    /**
102     * Get the transition infos to initialize creation/edition form fields
103     * @param workflowName the current workflow name
104     * @param transitionId the current transition's id, can be null
105     * @return the transition values and a list of taken ids
106     */
107    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
108    public Map<String, Object> getTransitionInfos(String workflowName, Integer transitionId)
109    {
110        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
111        
112        // Check user right
113        _workflowRightHelper.checkEditRight(workflowDescriptor);
114        
115        Map<String, Object> transitionInfos = new HashMap<>();
116        Set<Integer> transitionIds = _workflowHelper.getAllActions(workflowDescriptor);
117        I18nizableText labelKey = DEFAULT_ACTION_NAME;
118        if (transitionId != null)
119        {
120            transitionIds.remove(transitionId);
121            transitionInfos.put("id", transitionId);
122            ActionDescriptor action = workflowDescriptor.getAction(transitionId);
123            labelKey = getActionLabel(action);
124            
125            transitionInfos.put("finalStep", action.getUnconditionalResult().getStep());
126        }
127        transitionInfos.put("ids", transitionIds);
128        
129        Map<String, String> translations = _workflowSessionHelper.getTranslation(workflowName, labelKey);
130        if (translations == null)
131        {
132            translations = new HashMap<>();
133            translations.put(_workflowLanguageManager.getCurrentLanguage(), _i18nHelper.translateKey(workflowDescriptor.getName(), labelKey, DEFAULT_ACTION_NAME));
134        }
135        transitionInfos.put("labels", translations);
136        
137        return transitionInfos;
138    }
139    
140    /**
141     * Get a set of transitions already defined in current workflow beside initialActions
142     * @param workflowName the current workflow name
143     * @param parentStepId the current selected step
144     * @return a set containing maps of actions properties
145     */
146    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
147    public Set<Map<String, Object>> getAvailableActions(String workflowName, Integer parentStepId)
148    {
149        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
150        
151        // Check user right
152        _workflowRightHelper.checkReadRight(workflowDescriptor);
153        
154        
155        Set<Map<String, Object>> availableActions = new HashSet<>();
156        if (0 != parentStepId) // generic actions can't be used as  initial actions
157        {
158            StepDescriptor parentStep = workflowDescriptor.getStep(parentStepId);
159            List<ActionDescriptor> actions = parentStep.getActions();
160            
161            List<StepDescriptor> steps = workflowDescriptor.getSteps();
162            for (StepDescriptor step: steps)
163            {
164                List<ActionDescriptor> outgoingActions = step.getActions();
165                for (ActionDescriptor outgoingAction: outgoingActions)
166                {
167                    if (!actions.contains(outgoingAction))
168                    {
169                        availableActions.add(_getActionInfos(workflowName, outgoingAction));
170                    }
171                }
172            }
173        }
174        return availableActions;
175    }
176
177    private Map<String, Object> _getActionInfos(String workflowName, ActionDescriptor action)
178    {
179        Map<String, Object> actionInfos = new HashMap<>();
180        int actionId = action.getId();
181        actionInfos.put("id", actionId);
182        actionInfos.put("label", getActionLabel(workflowName, action) + " (" + actionId + ")");
183        return actionInfos;
184    }
185    
186    /**
187     * Create a new transition
188     * @param workflowName the current workflow name
189     * @param parentStepId the parent step id
190     * @param transitionId the id for the transition to create
191     * @param labels the multilinguals labels for the transition
192     * @param finalStepId the id for the transition's unconditional result
193     * @return a map with error message or with transition's id if succesfull
194     */
195    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
196    public Map<String, Object> createTransition(String workflowName, Integer parentStepId, int transitionId, Map<String, String> labels, int finalStepId)
197    {
198        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
199        
200        // Check user right
201        _workflowRightHelper.checkEditRight(workflowDescriptor);
202        
203        if (_workflowHelper.getAllActions(workflowDescriptor).contains(transitionId))
204        {
205            return Map.of("message", "duplicate-id");
206        }
207        
208        DescriptorFactory factory = new DescriptorFactory();
209        ActionDescriptor action = factory.createActionDescriptor();
210        action.setId(transitionId);
211        I18nizableText actionNameI18nKey = _i18nHelper.generateI18nKey(workflowName, "ACTION", transitionId);
212        action.setName(actionNameI18nKey.toString());
213        ResultDescriptor finalStep = factory.createResultDescriptor();
214        finalStep.setStep(finalStepId);
215        action.setUnconditionalResult(finalStep);
216        
217        if (isInitialStep(parentStepId))
218        {
219            workflowDescriptor.addInitialAction(action);
220        }
221        else
222        {
223            StepDescriptor stepDescriptor = workflowDescriptor.getStep(parentStepId);
224            stepDescriptor.getActions().add(action);
225        }
226        
227        _workflowSessionHelper.updateTranslations(workflowName, actionNameI18nKey, labels);
228        _generateActionDescription(workflowName, actionNameI18nKey, labels);
229        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
230        
231        return _getActionProperties(workflowDescriptor, action, parentStepId);
232    }
233
234    private Map<String, Object> _getActionProperties(WorkflowDescriptor workflowDescriptor, ActionDescriptor action, Integer stepId)
235    {
236        Map<String, Object> results = new HashMap<>();
237        results.put("actionId", action.getId());
238        results.put("actionLabels", getActionLabel(workflowDescriptor.getName(), action));
239        results.put("stepId", stepId);
240        results.put("stepLabel", _workflowStepDAO.getStepLabel(workflowDescriptor, stepId));
241        results.put("workflowId", workflowDescriptor.getName());
242        
243        return results;
244    }
245
246    private boolean isInitialStep(Integer stepIdToInt)
247    {
248        return stepIdToInt == 0;
249    }
250    
251    /**
252     * Add an existing transition to current step
253     * @param workflowName the current workflow name
254     * @param parentStepId the current selected step id
255     * @param transitionIds a list of ids corresponding to the transitions to add to current step
256     * @return the first transition id and its parent sted id
257     */
258    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
259    public Map<String, Object> addTransitions(String workflowName, Integer parentStepId, List<Integer> transitionIds)
260    {
261        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
262        
263        // Check user right
264        _workflowRightHelper.checkEditRight(workflowDescriptor);
265        
266        Map<String, Object> results = new HashMap<>();
267        
268        if (parentStepId != 0)
269        {
270            StepDescriptor step = workflowDescriptor.getStep(parentStepId);
271            for (Integer id : transitionIds)
272            {
273                ActionDescriptor action = _getAction(workflowDescriptor, id);
274                step.getActions().add(action);
275                if (!action.isCommon())
276                {
277                    _updateWorkflowCommonAction(workflowDescriptor, id, action);
278                }
279                else
280                {
281                    step.getCommonActions().add(id);
282                }
283            }
284            _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
285            results = _getActionProperties(workflowDescriptor, workflowDescriptor.getAction(transitionIds.get(0)), parentStepId);
286        }
287        else
288        {
289            results.put("message", "initial-step");
290        }
291        
292        return results;
293    }
294    
295    private ActionDescriptor _getAction(WorkflowDescriptor workflowDescriptor, Integer actionId)
296    {
297        ActionDescriptor action = workflowDescriptor.getAction(actionId);
298        if (action == null)
299        {
300            Map<Integer, ActionDescriptor> commonActions = workflowDescriptor.getCommonActions();
301            action = commonActions.get(actionId);
302        }
303        return action;
304    }
305    
306    /**
307     * Set action as 'common' in steps using it
308     * @param workflowDescriptor the current workflow
309     * @param actionId id of current action
310     * @param action the action
311     */
312    protected void _updateWorkflowCommonAction(WorkflowDescriptor workflowDescriptor, int actionId, ActionDescriptor action)
313    {
314        List<StepDescriptor> stepsToUpdate = new ArrayList<>();
315        List<StepDescriptor> steps = workflowDescriptor.getSteps();
316        
317        //remove individual action from steps
318        for (StepDescriptor step : steps)
319        {
320            if (step.getAction(actionId) != null)
321            {
322                step.getActions().remove(action);
323                stepsToUpdate.add(step);
324            }
325        }
326        //set action as common in workflow
327        workflowDescriptor.addCommonAction(action);
328        
329        //put back action in steps as common action
330        for (StepDescriptor step : stepsToUpdate)
331        {
332            step.getCommonActions().add(actionId);
333            step.getActions().add(action);
334        }
335    }
336    
337    /**
338     * Rename the transition
339     * @param workflowName the workflow name
340     * @param stepId id of selected step
341     * @param actionId the transition's id
342     * @param newMainLabel the new label in current language
343     * @return  a map with error message or with transition's infos if succesfull
344     */
345    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
346    public Map<String, Object> editTransitionLabel(String workflowName, Integer stepId, Integer actionId, String newMainLabel)
347    {
348        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
349        
350        // Check user right
351        _workflowRightHelper.checkEditRight(workflowDescriptor);
352        
353        ActionDescriptor action = workflowDescriptor.getAction(actionId);
354        _updateActionName(workflowName, action);
355        I18nizableText actionKey = getActionLabel(action);
356        _workflowSessionHelper.updateTranslations(workflowName, actionKey, Map.of(_workflowLanguageManager.getCurrentLanguage(), newMainLabel));
357        
358        _generateActionDescription(workflowName, actionKey, Map.of(_workflowLanguageManager.getCurrentLanguage(), newMainLabel));
359        
360        return _getActionProperties(workflowDescriptor, action, stepId);
361    }
362
363    private void _updateActionName(String workflowName, ActionDescriptor action)
364    {
365        String defaultCatalog = _workflowHelper.getWorkflowCatalog(workflowName);
366        I18nizableText actionKey = getActionLabel(action);
367        
368        if (!defaultCatalog.equals(actionKey.getCatalogue()))
369        {
370            String newName = new I18nizableText(defaultCatalog, actionKey.getKey()).toString();
371            action.setName(newName);
372        }
373    }
374    
375    /**
376     * Edit the transition
377     * @param workflowName the workflow name
378     * @param stepId id of selected step
379     * @param oldId the transition's former id
380     * @param newId the transition's new id
381     * @param labels the transition's multilingual labels
382     * @param finalStepId the transition's unconditional result id
383     * @return a map with error message or with transition's infos if succesfull
384     */
385    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
386    public Map<String, Object> editTransition(String workflowName, Integer stepId, Integer oldId, Integer newId, Map<String, String> labels, Integer finalStepId)
387    {
388        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
389        
390        // Check user right
391        _workflowRightHelper.checkEditRight(workflowDescriptor);
392        
393        ActionDescriptor action = workflowDescriptor.getAction(oldId);
394        
395        if (!newId.equals(oldId))
396        {
397            if (_workflowHelper.getAllActions(workflowDescriptor).contains(newId))
398            {
399                return Map.of("message", "duplicate-id");
400            }
401            if (action.isCommon())
402            {
403                //Don't change edit order below: first update steps then update action
404                workflowDescriptor.getCommonActions().remove(oldId, action);
405                List<StepDescriptor> steps = workflowDescriptor.getSteps();
406                for (StepDescriptor step: steps)
407                {
408                    if (step.getAction(oldId) != null)
409                    {
410                        List<Integer> commonActions = step.getCommonActions();
411                        commonActions.remove(commonActions.indexOf(oldId));
412                        commonActions.add(newId);
413                    }
414                }
415                action.setId(newId);
416                workflowDescriptor.getCommonActions().put(newId, action);
417            }
418            else
419            {
420                action.setId(newId);
421            }
422        }
423        
424        DescriptorFactory factory = new DescriptorFactory();
425        ResultDescriptor finalStep = factory.createResultDescriptor();
426        finalStep.setStep(finalStepId);
427        action.setUnconditionalResult(finalStep);
428        
429        _updateActionName(workflowName, action);
430        I18nizableText actionKey = getActionLabel(action);
431        _workflowSessionHelper.updateTranslations(workflowName, actionKey, labels);
432        
433        _generateActionDescription(workflowName, actionKey, labels);
434        
435        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
436        
437        return _getActionProperties(workflowDescriptor, action, stepId);
438    }
439    
440    private void _generateActionDescription(String workflowName, I18nizableText actionKey, Map<String, String> labels)
441    {
442        I18nizableText actionDescriptionI18nKey = new I18nizableText(actionKey.getCatalogue(), actionKey.getKey() + "_ACTION_DESCRIPTION");
443        
444        Map<String, String> descriptionValues = labels.entrySet()
445            .stream()
446            .collect(Collectors.toMap(
447                e -> e.getKey(),
448                e -> _getDescriptionValue(e.getValue(), e.getKey())
449            )
450        );
451        
452        _workflowSessionHelper.updateTranslations(workflowName, actionDescriptionI18nKey, descriptionValues);
453    }
454    
455    private String _getDescriptionValue(String label, String lang)
456    {
457        I18nizableText descriptionPrefixI18nText = new I18nizableText("plugin.workflow", "PLUGINS_WORKFLOW_CREATE_TRANSITION_DEFAULT_DESRIPTION", List.of(StringUtils.escapeHTML(label)));
458        return _i18nUtils.translate(descriptionPrefixI18nText, lang);
459    }
460    
461    /**
462     * Remove transition from step
463     * @param workflowName the current workflow name
464     * @param parentStepId the parent step id to remove the transition from
465     * @param transitionId the id for the transition to remove
466     * @return empty map if successfull
467     */
468    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
469    public Map<String, Object> removeTransition(String workflowName, Integer parentStepId, Integer transitionId)
470    {
471        WorkflowDescriptor workflowDescriptor = _workflowSessionHelper.getWorkflowDescriptor(workflowName, true);
472        
473        // Check user right
474        _workflowRightHelper.checkEditRight(workflowDescriptor);
475        
476        ActionDescriptor actionToRemove = null;
477        if (isInitialStep(parentStepId))
478        {
479            actionToRemove = workflowDescriptor.getInitialAction(transitionId);
480            _workflowSessionHelper.removeTranslation(workflowName, new I18nizableText("application", actionToRemove.getName()));
481            workflowDescriptor.getInitialActions().remove(actionToRemove);
482        }
483        else
484        {
485            StepDescriptor stepDescriptor = workflowDescriptor.getStep(parentStepId);
486            actionToRemove = workflowDescriptor.getAction(transitionId);
487            stepDescriptor.getActions().remove(actionToRemove);
488            if (actionToRemove.isCommon())
489            {
490                List<Integer> commonActions = stepDescriptor.getCommonActions();
491                commonActions.remove(commonActions.indexOf(transitionId));
492                _manageCommonAction(workflowDescriptor, actionToRemove);
493            }
494            else
495            {
496                I18nizableText actionKey = getActionLabel(actionToRemove);
497                _workflowSessionHelper.removeTranslation(workflowName, actionKey); //use actionKey instead of action.getName() in case plugin's name is in the name
498            }
499        }
500        _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
501        
502        return _getActionProperties(workflowDescriptor, actionToRemove, parentStepId);
503    }
504    
505    /**
506     * Verify that the action is still used by multiple steps. If not anymore, replace the action by a non common copy
507     * @param workflowDescriptor the current workflow
508     * @param action the action removed
509     */
510    protected void _manageCommonAction(WorkflowDescriptor workflowDescriptor, ActionDescriptor action)
511    {
512        Map<Integer, ActionDescriptor> commonActions = workflowDescriptor.getCommonActions();
513        Integer id = action.getId();
514        if (commonActions.containsKey(id))
515        {
516            List<StepDescriptor> steps = workflowDescriptor.getSteps();
517            int numberOfUse = _getNumberOfUse(id, steps);
518            if (numberOfUse == 1)
519            {
520                //only way to unset common in action is to replace it with a new copy
521                StepDescriptor parentStep = _getParentStep(steps, id).get();
522                List<Integer> stepCommonActions = parentStep.getCommonActions();
523                stepCommonActions.remove(stepCommonActions.indexOf(id));
524                parentStep.getActions().remove(action);
525                workflowDescriptor.getCommonActions().remove(id, action);
526                ActionDescriptor actionCopy =  _copyAction(action);
527                parentStep.getActions().add(actionCopy);
528                _workflowSessionHelper.updateWorkflowDescriptor(workflowDescriptor);
529            }
530        }
531    }
532    
533    private Optional<StepDescriptor> _getParentStep(List<StepDescriptor> steps, int actionId)
534    {
535        return steps.stream().filter(s -> s.getAction(actionId) != null).findFirst();
536    }
537
538    private ActionDescriptor _copyAction(ActionDescriptor actionToCopy)
539    {
540        DescriptorFactory factory = new DescriptorFactory();
541        ActionDescriptor action = factory.createActionDescriptor();
542        action.setId(actionToCopy.getId());
543        action.setName(actionToCopy.getName());
544        action.setUnconditionalResult(actionToCopy.getUnconditionalResult());
545        action.getConditionalResults().addAll(actionToCopy.getConditionalResults());
546        action.setMetaAttributes(actionToCopy.getMetaAttributes());
547        action.setRestriction(actionToCopy.getRestriction());
548        action.getPreFunctions().addAll(actionToCopy.getPreFunctions());
549        action.getPostFunctions().addAll(actionToCopy.getPostFunctions());
550        
551        return action;
552    }
553    
554    /**
555     * Get the number of steps using the action
556     * @param workflowName the current workflow's name
557     * @param actionId the current action's id
558     * @return the number of steps using the action
559     */
560    @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION)
561    public int getNumberOfUse(String workflowName, Integer actionId)
562    {
563        WorkflowDescriptor workflow = _workflowSessionHelper.getWorkflowDescriptor(workflowName, false);
564        
565        _workflowRightHelper.checkReadRight(workflow);
566        
567        return _getNumberOfUse(actionId, workflow.getSteps());
568    }
569    
570    private int _getNumberOfUse(Integer actionId, List<StepDescriptor> steps)
571    {
572        int stepSize = steps.size();
573        int numberOfUse = 0;
574        int index = 0;
575        while (index < stepSize)
576        {
577            if (steps.get(index).getAction(actionId) != null)
578            {
579                numberOfUse++;
580            }
581            index++;
582        }
583        return numberOfUse;
584    }
585    
586    /**
587     * Get the translated action label
588     * @param workflowName the workflow's unique name
589     * @param action current action
590     * @return the action label
591     */
592    public String getActionLabel(String workflowName, ActionDescriptor action)
593    {
594        I18nizableText label = getActionLabel(action);
595        return _i18nHelper.translateKey(workflowName, label, DEFAULT_ACTION_NAME);
596    }
597    
598    /**
599     * Get the action's icon path
600     * @param workflowName name of current workflow
601     * @param action current action
602     * @return the icon's path
603     */
604    public String getActionIconPath(String workflowName, ActionDescriptor action)
605    {
606        I18nizableText label = getActionLabel(action);
607        label = _workflowSessionHelper.getOldLabelKeyIfCloned(workflowName, label);
608        return _workflowHelper.getElementIconPath(label, __DEFAULT_ACTION_ICON_PATH);
609    }
610    
611    /**
612     * Get the action's icon path as base 64 for svg's links
613     * @param workflowName name of current workflow
614     * @param action current action
615     * @return the icon's path as base 64
616     */
617    public String getActionIconPathAsBase64(String workflowName, ActionDescriptor action)
618    {
619        I18nizableText label = getActionLabel(action);
620        label = _workflowSessionHelper.getOldLabelKeyIfCloned(workflowName, label);
621        return _workflowHelper.getElementIconAsBase64(label, __DEFAULT_SVG_ACTION_ICON_PATH);
622    }
623    
624    /**
625     * Get the action label as i18n
626     * @param action the current action
627     * @return the label as i18n
628     */
629    public I18nizableText getActionLabel(ActionDescriptor action)
630    {
631        return new I18nizableText("application", action.getName());
632    }
633}