001/* 002 * Copyright 2016 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.forms.workflow; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.stream.Collectors; 024 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.configuration.DefaultConfiguration; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.commons.collections.ListUtils; 031 032import org.ametys.cms.clientsideelement.WorkflowStepsClientSideElement; 033import org.ametys.cms.repository.Content; 034import org.ametys.cms.workflow.AbstractContentWorkflowComponent; 035import org.ametys.core.ui.Callable; 036import org.ametys.plugins.forms.Form; 037import org.ametys.plugins.forms.FormsException; 038import org.ametys.plugins.forms.data.FieldValue; 039import org.ametys.plugins.forms.data.UserEntry; 040import org.ametys.plugins.forms.jcr.FormPropertiesManager; 041import org.ametys.plugins.forms.table.FormTableManager; 042import org.ametys.plugins.workflow.AbstractWorkflowComponent; 043import org.ametys.plugins.workflow.store.JdbcWorkflowStore; 044import org.ametys.runtime.i18n.I18nizableText; 045 046import com.opensymphony.workflow.Workflow; 047import com.opensymphony.workflow.loader.ActionDescriptor; 048import com.opensymphony.workflow.loader.WorkflowDescriptor; 049import com.opensymphony.workflow.spi.Step; 050 051/** 052 * This class creates multiple toggle buttons each representing a workflow step on form entries 053 */ 054public class FormEntriesWorkflowStepsClientSideElement extends WorkflowStepsClientSideElement 055{ 056 /** The manager for the forms JCR representation */ 057 protected FormPropertiesManager _formPropertiesManager; 058 /** The manager for the forms SQL tables */ 059 protected FormTableManager _formTableManager; 060 061 @Override 062 public void service(ServiceManager serviceManager) throws ServiceException 063 { 064 super.service(serviceManager); 065 _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE); 066 _formTableManager = (FormTableManager) serviceManager.lookup(FormTableManager.ROLE); 067 } 068 069 @Override 070 protected String _getDefaultMenuClassName() 071 { 072 return "Ametys.plugins.forms.workflow.FormEntriesWorkflowMenu"; 073 } 074 075 @Override 076 protected String _getDefaultActionClassName() 077 { 078 return "Ametys.plugins.forms.workflow.FormEntriesWorkflowAction.doAction"; 079 } 080 081 @Override 082 protected void _configureWorkflowStep(WorkflowDescriptor workflowDescriptor, Integer stepId, Map<String, Object> stepParameters, Configuration stepConfiguration, List<Integer> allowedActionIds) throws ConfigurationException 083 { 084 super._configureWorkflowStep(workflowDescriptor, stepId, stepParameters, stepConfiguration, allowedActionIds); 085 086 List<Integer> sendMailIds = new ArrayList<>(); 087 088 List<Integer> currentStepActions = workflowDescriptor.getStep(stepId) 089 .getActions() 090 .stream() 091 .mapToInt(action -> ((ActionDescriptor) action).getId()) 092 .boxed() 093 .collect(Collectors.toList()); 094 List<Integer> allowedStepActions = ListUtils.intersection(currentStepActions, allowedActionIds); 095 096 if (stepConfiguration != null) 097 { 098 Configuration stepWorkflowSendMail = stepConfiguration.getChild("send-mail", false); 099 if (stepWorkflowSendMail != null) 100 { 101 sendMailIds.addAll(_configureWorkflowStepActions(workflowDescriptor.getName(), stepId, currentStepActions, allowedStepActions, stepWorkflowSendMail)); 102 } 103 else 104 { 105 sendMailIds.addAll(allowedStepActions); 106 } 107 } 108 else 109 { 110 sendMailIds.addAll(allowedStepActions); 111 } 112 113 stepParameters.put("workflow-send-mail-ids", sendMailIds); 114 } 115 116 117 118 @Override 119 protected Script _configureScript(Configuration configuration) throws ConfigurationException 120 { 121 List<ScriptFile> scriptsImports = _configureImports(configuration.getChild("scripts")); 122 List<ScriptFile> cssImports = _configureImports(configuration.getChild("css")); 123 124 Script script = new Script(this.getId(), "", scriptsImports, cssImports, new HashMap<>()); 125 List<ScriptFile> scriptFiles = new ArrayList<>(); 126 scriptFiles.add(new ScriptFile("/plugins/forms/resources/js/Ametys/plugins/forms/workflow/FormEntriesWorkflowMenu.js")); 127 scriptFiles.add(new ScriptFile("/plugins/forms/resources/js/Ametys/plugins/forms/workflow/FormEntriesWorkflowAction.js")); 128 script.getScriptFiles().addAll(scriptFiles); 129 130 return script; 131 } 132 133 @Override 134 protected String _getSelectionTargetId() 135 { 136 return "^(content-form)$"; 137 } 138 139 @Override 140 protected String _getDefaultPluginName() 141 { 142 return "forms"; 143 } 144 145 @SuppressWarnings("unchecked") 146 @Override 147 protected void _additionalMenuItemConfiguration(Configuration itemConf, DefaultConfiguration classConf, int actionId, Map<String, Object> parameters) 148 { 149 super._additionalMenuItemConfiguration(itemConf, classConf, actionId, parameters); 150 151 // Multiselection enabled ? 152 DefaultConfiguration multiselectConf = new DefaultConfiguration("selection-enable-multiselection"); 153 multiselectConf.setValue(itemConf.getChild("menu-" + actionId + "-enable-multiselection").getValueAsBoolean(true)); 154 classConf.addChild(multiselectConf); 155 156 // Send mail 157 DefaultConfiguration sendMailConf = new DefaultConfiguration("send-mail"); 158 sendMailConf.setValue(itemConf.getChild("menu-" + actionId + "-send-mail").getValueAsBoolean(((List<Integer>) parameters.get("workflow-send-mail-ids")).contains(actionId))); 159 classConf.addChild(sendMailConf); 160 161 // Dialog title for the mails 162 if (itemConf.getChild("menu-" + actionId + "-dialog-title", false) != null) 163 { 164 DefaultConfiguration dialogTitleConf = new DefaultConfiguration("dialog-title"); 165 dialogTitleConf.setAttribute("i18n", itemConf.getChild("menu-" + actionId + "-dialog-title").getAttribute("i18n", "true")); 166 dialogTitleConf.setValue(itemConf.getChild("menu-" + actionId + "-dialog-title").getValue(null)); 167 classConf.addChild(dialogTitleConf); 168 } 169 170 // i18n key for the subject 171 if (itemConf.getChild("menu-" + actionId + "-subject-key", false) != null) 172 { 173 DefaultConfiguration subjectKeyConf = new DefaultConfiguration("subject-key"); 174 subjectKeyConf.setAttribute("i18n", itemConf.getChild("menu-" + actionId + "-subject-key").getAttribute("i18n", "true")); 175 subjectKeyConf.setValue(itemConf.getChild("menu-" + actionId + "-subject-key").getValue(null)); 176 classConf.addChild(subjectKeyConf); 177 } 178 179 // i18n key for the body 180 if (itemConf.getChild("menu-" + actionId + "-body-key", false) != null) 181 { 182 DefaultConfiguration bodyKeyConf = new DefaultConfiguration("body-key"); 183 bodyKeyConf.setAttribute("i18n", itemConf.getChild("menu-" + actionId + "-body-key").getAttribute("i18n", "true")); 184 bodyKeyConf.setValue(itemConf.getChild("menu-" + actionId + "-body-key").getValue(null)); 185 classConf.addChild(bodyKeyConf); 186 } 187 188 } 189 190 @Override 191 protected void _configureDefaultDescriptions(Map<String, Object> parameters) 192 { 193 // One entry selected 194 if (!parameters.containsKey("entryselected-description")) 195 { 196 parameters.put("entryselected-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_DESCRIPTION")); 197 } 198 199 // Several entries selected 200 if (!parameters.containsKey("several-entryselected-description")) 201 { 202 parameters.put("several-entryselected-description", new I18nizableText("plugin.forms", "ENTRIES_WORKFLOW_DESCRIPTION")); 203 } 204 205 // Empty selection 206 if (!parameters.containsKey("selection-description-empty")) 207 { 208 parameters.put("selection-description-empty", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOSELECTION_DESCRIPTION")); 209 } 210 211 // No match selection 212 if (!parameters.containsKey("selection-description-nomatch")) 213 { 214 parameters.put("selection-description-nomatch", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOMATCH_DESCRIPTION")); 215 } 216 217 // Multiselection forbidden 218 if (!parameters.containsKey("selection-description-multiselectionforbidden")) 219 { 220 parameters.put("selection-description-multiselectionforbidden", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOMULTISELECT_DESCRIPTION")); 221 } 222 223 // No available action 224 if (!parameters.containsKey("noaction-available-description")) 225 { 226 parameters.put("noaction-available-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOACTIONAVAILABLE_DESCRIPTION")); 227 } 228 229 // Refreshing 230 if (!parameters.containsKey("refreshing-description")) 231 { 232 parameters.put("refreshing-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_REFRESH_DESCRIPTION")); 233 } 234 } 235 236 /** 237 * Get the workflow state of contents 238 * @param entryIds The ids of the selected entries 239 * @param formId The id of the form 240 * @param scriptId The id of the script 241 * @return The workflow state as a map 242 */ 243 @Callable 244 @SuppressWarnings("unchecked") 245 public Map<String, Object> getWorkflowState(List<Integer> entryIds, String formId, String scriptId) 246 { 247 List<Map<String, Object>> entries = new ArrayList<> (); 248 249 List<UserEntry> allEntries = new ArrayList<> (); 250 Workflow workflow = _workflowProvider.getExternalWorkflow(JdbcWorkflowStore.ROLE); 251 252 try 253 { 254 Form form = _formPropertiesManager.getForm(formId); 255 Map<String, FieldValue> columns = _formTableManager.getColumns(form); 256 257 allEntries = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, entryIds); 258 259 for (UserEntry entry : allEntries) 260 { 261 for (Script script : _scripts) 262 { 263 if (script.getId().equals(scriptId)) 264 { 265 Map<String, Object> entryMap = new HashMap<> (); 266 267 Integer workflowId = entry.getWorkflowId(); 268 if (workflowId != null) 269 { 270 List<Step> steps = workflow.getCurrentSteps(workflowId); 271 List<Integer> stepIds = new ArrayList<>(); 272 for (Step step : steps) 273 { 274 stepIds.add(step.getStepId()); 275 } 276 277 Integer workflowStepId = (Integer) script.getParameters().get("workflow-step"); 278 if (stepIds.contains(workflowStepId)) 279 { 280 Map<String, Object> transientVars = new HashMap<>(); 281 282 // Used to check the rights on the form's content 283 Content formContent = _formPropertiesManager.getFormContent(form.getId()); 284 transientVars.put(AbstractContentWorkflowComponent.CONTENT_KEY, formContent); 285 transientVars.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList<String> ()); 286 287 int[] availableActions = workflow.getAvailableActions(workflowId, transientVars); 288 Arrays.sort(availableActions); 289 290 List<Integer> activeActions = new ArrayList<>(); 291 for (int actionId : (List<Integer>) script.getParameters().get("workflow-actions-ids")) 292 { 293 if (Arrays.binarySearch(availableActions, actionId) >= 0) 294 { 295 activeActions.add(actionId); 296 } 297 } 298 299 entryMap.put("id", entry.getId()); 300 entryMap.put("actions", activeActions); 301 entries.add(entryMap); 302 } 303 } 304 } 305 } 306 } 307 } 308 catch (FormsException e) 309 { 310 getLogger().error("Error getting forms for a content.", e); 311 } 312 313 Map<String, Object> results = new HashMap<> (); 314 results.put("entries", entries); 315 return results; 316 } 317}