001/* 002 * Copyright 2017 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.bpm.process; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.cocoon.ProcessingException; 029import org.apache.cocoon.environment.ObjectModelHelper; 030import org.apache.cocoon.environment.Request; 031import org.apache.cocoon.generation.ServiceableGenerator; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.XMLUtils; 034import org.apache.commons.lang.StringUtils; 035import org.xml.sax.SAXException; 036 037import org.ametys.core.right.RightManager; 038import org.ametys.core.right.RightManager.RightResult; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.User; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.core.user.UserManager; 043import org.ametys.core.util.DateUtils; 044import org.ametys.plugins.bpm.BPMWorkflowManager; 045import org.ametys.plugins.bpm.jcr.JCRWorkflow; 046import org.ametys.plugins.bpm.jcr.JCRWorkflowProcess; 047import org.ametys.plugins.core.user.UserHelper; 048import org.ametys.plugins.explorer.resources.ModifiableResource; 049import org.ametys.plugins.explorer.resources.ResourceCollection; 050import org.ametys.plugins.repository.AmetysObjectIterable; 051import org.ametys.plugins.repository.AmetysObjectResolver; 052import org.ametys.plugins.repository.AmetysRepositoryException; 053import org.ametys.plugins.workflow.support.WorkflowProvider; 054import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 055import org.ametys.runtime.authentication.AccessDeniedException; 056import org.ametys.runtime.authentication.AuthorizationRequiredException; 057 058import com.opensymphony.workflow.loader.ActionDescriptor; 059import com.opensymphony.workflow.loader.StepDescriptor; 060import com.opensymphony.workflow.loader.WorkflowDescriptor; 061import com.opensymphony.workflow.spi.Step; 062 063/** 064 * Sax a process data 065 */ 066public class ProcessGenerator extends ServiceableGenerator 067{ 068 private static final String __MESSAGE_NO_ACTION = "plugin.cms:WORKFLOW_UNKNOWN_ACTION"; 069 070 private BPMWorkflowManager _bpmWorkflowManager; 071 private AmetysObjectResolver _resolver; 072 private UserManager _userManager; 073 private WorkflowProvider _workflowProvider; 074 private UserHelper _userHelper; 075 private CurrentUserProvider _currentUserProvider; 076 private RightManager _rightManager; 077 078 @Override 079 public void service(ServiceManager smanager) throws ServiceException 080 { 081 super.service(smanager); 082 _bpmWorkflowManager = (BPMWorkflowManager) smanager.lookup(BPMWorkflowManager.ROLE); 083 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 084 _userManager = (UserManager) smanager.lookup(UserManager.ROLE); 085 _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE); 086 _userHelper = (UserHelper) smanager.lookup(UserHelper.ROLE); 087 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 088 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 089 } 090 091 public void generate() throws IOException, SAXException, ProcessingException 092 { 093 String workflowName = parameters.getParameter("workflowName", null); 094 String processName = parameters.getParameter("processName", null); 095 096 if (workflowName == null || processName == null) 097 { 098 throw new IllegalArgumentException("Missing mandator arguments for saxing a process informations"); 099 } 100 101 JCRWorkflowProcess process = _bpmWorkflowManager.getProcess(workflowName, processName); 102 103 if (process == null) 104 { 105 throw new IllegalArgumentException("No process found for workflow name '" + workflowName + "' and process name '" + processName + "'"); 106 } 107 108 UserIdentity user = _currentUserProvider.getUser(); 109 if (user == null) 110 { 111 throw new AuthorizationRequiredException("Access denied to process '" + workflowName + "/" + processName + "' for anonymous user"); 112 } 113 114 JCRWorkflow workflow = _resolver.resolveById(process.getWorkflow()); 115 116 if (_rightManager.currentUserHasRight(BPMWorkflowManager.RIGHTS_PROCESS_MANAGE_PROCESSES, null) != RightResult.RIGHT_ALLOW 117 && !user.equals(process.getCreator()) && !_bpmWorkflowManager.isUserInWorkflowVariables(workflow)) 118 { 119 throw new AccessDeniedException("Access denied to process '" + workflowName + "/" + processName + "' for user '" + user + "'"); 120 } 121 122 contentHandler.startDocument(); 123 _saxProcess(processName, process, workflow); 124 contentHandler.endDocument(); 125 } 126 127 private void _saxProcess(String processName, JCRWorkflowProcess process, JCRWorkflow workflow) throws SAXException 128 { 129 boolean isComplete = _bpmWorkflowManager.isComplete(process); 130 131 AttributesImpl attrs = new AttributesImpl(); 132 attrs.addCDATAAttribute("id", process.getId()); 133 attrs.addCDATAAttribute("name", processName); 134 attrs.addCDATAAttribute("complete", String.valueOf(isComplete)); 135 XMLUtils.startElement(contentHandler, "process", attrs); 136 137 // Add the workflow used by the process in the request, to be used by any RegisterVariable of the workflow definition 138 Request request = ObjectModelHelper.getRequest(objectModel); 139 request.setAttribute("workflowId", process.getWorkflow()); 140 141 AmetysObjectWorkflow aoWorkflow = _workflowProvider.getAmetysObjectWorkflow(process, true); 142 WorkflowDescriptor workflowDescriptor = _getWorkflowDescriptor(aoWorkflow, process); 143 144 _saxProcess(process, workflow); 145 if (workflowDescriptor != null) 146 { 147 _saxWorkflowSteps(process, aoWorkflow, workflowDescriptor); 148 _saxCurrentWorkflowStep(process, workflowDescriptor); 149 if (!isComplete) 150 { 151 _saxWorkflowActions(process, workflow, aoWorkflow, workflowDescriptor); 152 } 153 } 154 155 if (_bpmWorkflowManager.canDelete(process)) 156 { 157 XMLUtils.createElement(contentHandler, "action-delete", "true"); 158 } 159 160 XMLUtils.endElement(contentHandler, "process"); 161 } 162 163 private WorkflowDescriptor _getWorkflowDescriptor(AmetysObjectWorkflow aoWorkflow, JCRWorkflowProcess process) throws AmetysRepositoryException 164 { 165 try 166 { 167 String workflowName = aoWorkflow.getWorkflowName(process.getWorkflowId()); 168 169 if (workflowName == null) 170 { 171 return null; 172 } 173 174 return aoWorkflow.getWorkflowDescriptor(workflowName); 175 } 176 catch (AmetysRepositoryException e) 177 { 178 // no workflow node for process 179 return null; 180 } 181 } 182 183 private void _saxProcess(JCRWorkflowProcess process, JCRWorkflow workflow) throws SAXException 184 { 185 AttributesImpl attrs = new AttributesImpl(); 186 attrs.addCDATAAttribute("id", workflow.getId()); 187 attrs.addCDATAAttribute("name", workflow.getName()); 188 XMLUtils.createElement(contentHandler, "workflow", attrs, workflow.getTitle()); 189 190 XMLUtils.createElement(contentHandler, "title", process.getTitle()); 191 UserIdentity creatorIdentity = process.getCreator(); 192 User creator = _userManager.getUser(creatorIdentity); 193 if (creator != null) 194 { 195 attrs = new AttributesImpl(); 196 attrs.addCDATAAttribute("id", UserIdentity.userIdentityToString(creatorIdentity)); 197 XMLUtils.createElement(contentHandler, "creator", attrs, creator.getFullName()); 198 } 199 else if (creatorIdentity != null) 200 { 201 XMLUtils.createElement(contentHandler, "creator", UserIdentity.userIdentityToString(creatorIdentity)); 202 } 203 204 String description = process.getDescription(); 205 if (description != null) 206 { 207 XMLUtils.createElement(contentHandler, "description", description); 208 } 209 210 ResourceCollection rootAttachments = process.getRootAttachments(false); 211 if (rootAttachments != null) 212 { 213 XMLUtils.startElement(contentHandler, "attachments"); 214 AmetysObjectIterable<ModifiableResource> attachments = rootAttachments.getChildren(); 215 for (ModifiableResource attachment : attachments) 216 { 217 AttributesImpl attachmentAttrs = new AttributesImpl(); 218 attachmentAttrs.addCDATAAttribute("name", attachment.getName()); 219 XMLUtils.createElement(contentHandler, "attachment", attachmentAttrs); 220 } 221 XMLUtils.endElement(contentHandler, "attachments"); 222 } 223 } 224 225 private void _saxWorkflowSteps(JCRWorkflowProcess process, AmetysObjectWorkflow aoWorkflow, WorkflowDescriptor workflowDescriptor) throws SAXException 226 { 227 List<Step> steps = _getProcessSteps(process, aoWorkflow); 228 229 Step previousStep = null; 230 XMLUtils.startElement(contentHandler, "steps"); 231 for (Step step : steps) 232 { 233 _saxStep(process, step, previousStep, workflowDescriptor); 234 previousStep = step; 235 } 236 XMLUtils.endElement(contentHandler, "steps"); 237 } 238 239 private List<Step> _getProcessSteps(JCRWorkflowProcess process, AmetysObjectWorkflow aoWorkflow) 240 { 241 long workflowId = process.getWorkflowId(); 242 243 @SuppressWarnings("unchecked") 244 List<Step> allSteps = new ArrayList(aoWorkflow.getCurrentSteps(workflowId)); 245 allSteps.addAll(aoWorkflow.getHistorySteps(workflowId)); 246 247 // Sort by start date descendant (only relevant when there is no 248 // split and nor join!) 249 Collections.sort(allSteps, new Comparator<Step>() 250 { 251 public int compare(Step s1, Step s2) 252 { 253 return s1.getStartDate().compareTo(s2.getStartDate()); 254 } 255 }); 256 257 return allSteps; 258 } 259 260 private void _saxStep(JCRWorkflowProcess process, Step step, Step previousStep, WorkflowDescriptor workflowDescriptor) throws SAXException 261 { 262 XMLUtils.startElement(contentHandler, "step"); 263 264 XMLUtils.createElement(contentHandler, "startDate", DateUtils.dateToString(step.getStartDate())); 265 266 UserIdentity caller = previousStep != null ? UserIdentity.stringToUserIdentity(previousStep.getCaller()) : process.getCreator(); 267 String userFullName = _userHelper.getUserFullName(caller); 268 XMLUtils.createElement(contentHandler, "user", StringUtils.isNotEmpty(userFullName) ? userFullName : UserIdentity.userIdentityToString(caller)); 269 270 271 if (previousStep != null) 272 { 273 int actionId = previousStep.getActionId(); 274 275 AttributesImpl attrs = new AttributesImpl(); 276 attrs.addCDATAAttribute("id", Integer.toString(actionId)); 277 XMLUtils.startElement(contentHandler, "action", attrs); 278 279 ActionDescriptor actionDescriptor = workflowDescriptor.getAction(actionId); 280 XMLUtils.createElement(contentHandler, "label", actionDescriptor == null ? __MESSAGE_NO_ACTION : actionDescriptor.getName() + "_ACTION_DESCRIPTION"); 281 XMLUtils.endElement(contentHandler, "action"); 282 } 283 else 284 { 285 XMLUtils.startElement(contentHandler, "action"); 286 XMLUtils.createElement(contentHandler, "label", "plugin.bpm:WORKFLOW_ACTION_CREATE_ACTION_DESCRIPTION"); 287 XMLUtils.endElement(contentHandler, "action"); 288 } 289 290 XMLUtils.endElement(contentHandler, "step"); 291 } 292 293 private void _saxCurrentWorkflowStep(JCRWorkflowProcess process, WorkflowDescriptor workflowDescriptor) throws SAXException 294 { 295 StepDescriptor step = workflowDescriptor.getStep((int) process.getCurrentStepId()); 296 XMLUtils.createElement(contentHandler, "currentStep", step.getName()); 297 } 298 299 private void _saxWorkflowActions(JCRWorkflowProcess process, JCRWorkflow workflow, AmetysObjectWorkflow aoWorkflow, WorkflowDescriptor workflowDescriptor) throws SAXException 300 { 301 XMLUtils.startElement(contentHandler, "actions"); 302 303 Map<String, Object> inputs = new HashMap<>(); 304 inputs.put("workflowId", workflow.getId()); 305 inputs.put("process", process); 306 int[] availableActions = aoWorkflow.getAvailableActions(process.getWorkflowId(), inputs); 307 for (int actionId : availableActions) 308 { 309 ActionDescriptor action = workflowDescriptor.getAction(actionId); 310 311 if (action != null) 312 { 313 AttributesImpl attrs = new AttributesImpl(); 314 attrs.addCDATAAttribute("id", Integer.toString(actionId)); 315 attrs.addCDATAAttribute("name", action.getName()); 316 XMLUtils.createElement(contentHandler, "action", attrs); 317 } 318 } 319 320 XMLUtils.endElement(contentHandler, "actions"); 321 } 322 323}