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.readers;
017
018import java.io.ByteArrayOutputStream;
019import java.io.IOException;
020import java.nio.charset.StandardCharsets;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.avalon.framework.service.Serviceable;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.reading.AbstractReader;
029import org.apache.commons.io.IOUtils;
030import org.xml.sax.SAXException;
031
032import org.ametys.core.util.I18nUtils;
033import org.ametys.plugins.workflow.dao.WorkflowStepDAO;
034import org.ametys.plugins.workflow.support.WorkflowHelper;
035import org.ametys.runtime.i18n.I18nizableText;
036
037import com.opensymphony.workflow.loader.ActionDescriptor;
038import com.opensymphony.workflow.loader.StepDescriptor;
039import com.opensymphony.workflow.loader.WorkflowDescriptor;
040
041import net.sourceforge.plantuml.FileFormat;
042import net.sourceforge.plantuml.FileFormatOption;
043import net.sourceforge.plantuml.SourceStringReader;
044
045/**
046 * Abstract class for reading PlantUML SVG
047 */
048public abstract class AbstractPlantUMLSVGReader extends AbstractReader implements Serviceable
049{
050    /** The color for step nodes */
051    protected static final String __MAIN_STEP_NODE_COLOR = "#e5f8ff";
052    
053    /** The workflow helper */
054    protected WorkflowHelper _workflowHelper;
055    
056    /** I18n Utils */
057    protected I18nUtils _i18nUtils;
058    
059    /** The workflow step DAO */
060    protected WorkflowStepDAO _workflowStepDAO;
061    
062    @Override
063    public void service(ServiceManager serviceManager) throws ServiceException
064    {
065        _workflowHelper = (WorkflowHelper) serviceManager.lookup(WorkflowHelper.ROLE);
066        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
067        _workflowStepDAO = (WorkflowStepDAO) serviceManager.lookup(WorkflowStepDAO.ROLE);
068    }
069    
070    @Override
071    public void generate() throws IOException, SAXException, ProcessingException
072    {
073        try
074        {
075            Request request = ObjectModelHelper.getRequest(objectModel);
076            String workflowName = (String) request.get("workflowName");
077            
078            WorkflowDescriptor workflowDescriptor = _workflowHelper.getWorkflowDescriptor(workflowName);
079            
080            _setPlantUMLProperties();
081            String content = _getPlantUMLContent(request, workflowDescriptor);
082            SourceStringReader reader = new SourceStringReader(content);
083            
084            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream())
085            {
086                reader.outputImage(byteArrayOutputStream, new FileFormatOption(FileFormat.SVG).withSvgLinkTarget("_self"));
087                
088                String svgContent = byteArrayOutputStream.toString(StandardCharsets.UTF_8);
089                svgContent = svgContent.replaceAll("<svg ", "<svg onclick=\"parent.Ametys.tool.ToolsManager.getTool('uitool-workflow-preview').focus();\" ");
090                
091                IOUtils.write(svgContent, out, StandardCharsets.UTF_8);
092            }
093        }
094        finally
095        {
096            out.close();
097        }
098    }
099
100    /**
101     * Set common plantUML svg properties
102     */
103    protected void _setPlantUMLProperties()
104    {
105        System.setProperty("PLANTUML_ALLOW_JAVASCRIPT_IN_LINK", "true");
106        System.setProperty("PLANTUML_LIMIT_SIZE", String.valueOf(Integer.MAX_VALUE));
107    }
108
109    /**
110     * Get the PlantUML SVG content to read
111     * @param request the request 
112     * @param workflowDescriptor descriptor of current workflow
113     * @return The content to read
114     */
115    protected String _getPlantUMLContent(Request request, WorkflowDescriptor workflowDescriptor)
116    {
117        StringBuilder content = new StringBuilder("@start" + _getPlantUMLType() + "\n");
118        content.append(_getPlantUMLStyle());
119        content.append(_getPlantUMLGraphContent(request, workflowDescriptor));
120        content.append("@end" + _getPlantUMLType());
121        return content.toString();
122    }
123    
124    /**
125     * Get the diagram type
126     * @return the diagram type
127     */
128    protected abstract String _getPlantUMLType();
129
130    /**
131     * Get plantUML style for current diagram
132     * @return the style as string
133     */
134    protected abstract String _getPlantUMLStyle();
135
136    /**
137     * Get the plantUML diagram body
138     * @param request the request 
139     * @param workflowDescriptor descriptor of current workflow
140     * @return the diagram body as string
141     */
142    protected abstract String _getPlantUMLGraphContent(Request request, WorkflowDescriptor workflowDescriptor);
143
144    /**
145     * Get js function to send selection
146     * @param workflowName unique name of current workflow
147     * @param stepId current or incoming step's id 
148     * @param actionId current action's, can be null
149     * @return the js function 
150     */
151    protected String _getJsFunction(String workflowName, String stepId, String actionId)
152    {
153        String jsParameters = "'" + workflowName + "','" + stepId;
154        jsParameters += actionId != null ? "','" + actionId + "'" : "'";
155        return "Ametys.plugins.workflow.UI.UIWorkflowGraph.sendSelection(" + jsParameters + ")";
156    }
157    
158    /**
159     * Get the node label for step 
160     * @param workflowDescriptor current workflow
161     * @param stepId current step id
162     * @return the node label which is the translated step name and its id
163     */
164    protected String _getStepNodeLabel(WorkflowDescriptor workflowDescriptor, int stepId) 
165    {
166        return _workflowStepDAO.getStepLabel(workflowDescriptor, stepId) + " (" + stepId + ")";
167    }
168
169    /**
170     * Get the tooltip for a link on step
171     * @param step current step
172     * @return the tooltip
173     */
174    protected String _getStepTooltip(StepDescriptor step)
175    {
176        return _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGIN_WORKFLOW_LINK_SEE_STEP"));
177    }
178    
179    /**
180     * Get the action label for node
181     * @param action current action
182     * @return the label as action's name and id 
183     */
184    protected String _getActionLabel(ActionDescriptor action)
185    {
186        return _workflowStepDAO.getActionLabel(action) + " (" + action.getId() + ")";
187    }
188    
189    /**
190     * Get the tooltip for a link on action
191     * @param action current action
192     * @return the tooltip
193     */
194    protected String _getActionTooltip(ActionDescriptor action)
195    {
196        return _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGIN_WORKFLOW_LINK_SEE_TRANSITION"));
197    }
198}