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