/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.cms.applications.mydesktoptool.actions; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; import org.infoglue.cms.applications.common.VisualFormatter; import org.infoglue.cms.applications.common.actions.InfoGlueAbstractAction; import org.infoglue.cms.controllers.kernel.impl.simple.ShortcutController; import org.infoglue.cms.controllers.kernel.impl.simple.WorkflowController; import org.infoglue.cms.entities.mydesktop.WorkflowActionVO; import org.infoglue.cms.entities.mydesktop.WorkflowStepVO; import org.infoglue.cms.entities.mydesktop.WorkflowVO; import org.infoglue.cms.exception.SystemException; import org.infoglue.cms.util.CmsPropertyHandler; import org.infoglue.cms.util.sorters.ReflectionComparator; import org.infoglue.cms.util.workflow.StepFilter; import webwork.action.ActionContext; import com.opensymphony.workflow.InvalidActionException; import com.opensymphony.workflow.WorkflowException; /** * This class implements the action class for the startpage in the mydesktop tool. * @author Mattias Bogeblad * @author <a href="mailto:jedprentice@gmail.com">Jed Prentice</a> */ public class ViewMyDesktopToolStartPageAction extends InfoGlueAbstractAction { private final static Logger logger = Logger.getLogger(ViewMyDesktopToolStartPageAction.class.getName()); private static final long serialVersionUID = 6543209932597662088L; protected static final String INVALID_ACTION = "invalidAction"; private static final WorkflowController controller = WorkflowController.getController(); private static final ShortcutController shortcutController = ShortcutController.getController(); private static final VisualFormatter formatter = new VisualFormatter(); private List availableWorkflowVOList; private List workflowVOList; private List availableShortcutVOList; private WorkflowVO workflow = new WorkflowVO(); private int actionId; private String finalReturnAddress = ""; public List getWorkflowVOList() { Collections.sort(workflowVOList, new ReflectionComparator("workflowId")); return workflowVOList; } public List getAvailableWorkflowVOList() { return availableWorkflowVOList; } public List getAvailableShortcutVOList() { return availableShortcutVOList; } public List getWorkflowActionVOList() { return getAvailableActions(null); } public List getWorkflowActionVOList(StepFilter filter) { return getAvailableActions(filter); } /** * Provides access to the underlying workflow * @return a reference to the underlying workflow */ WorkflowVO getWorkflow() { return workflow; } /** * Allows the workflow name to be set via the request parameter "workflowName". * @param name the name of the desired workflow */ public void setWorkflowName(String name) { workflow.setName(name); } private String getWorkflowName() { return workflow.getName(); } /** * Allows the action ID to be set via the request parameter "actionId" * @param actionId the ID of the action to execute */ public void setActionId(int actionId) { this.actionId = actionId; } /** * Allows the workflowID to be set via the request parameter "workflowId" * @param workflowId the ID of the desired workflow */ public void setWorkflowId(long workflowId) { workflow.setWorkflowId(new Long(workflowId)); } private long getWorkflowId() { return workflow.getIdAsPrimitive(); } /** * Populates the lists of workflow and workflow action VOs. * @return Action.SUCCESS * @throws SystemException if a workflow error occurs */ public String doExecute() throws SystemException { populateLists(); return SUCCESS; } /** * Populates the lists of workflow and workflow action VOs. * @return Action.SUCCESS * @throws SystemException if a workflow error occurs */ public String doTaskList() throws SystemException { populateActiveWorkflowVOList(); return "successTaskList"; } /** * Starts the workflow specified in the request parameter "workflowName" with the initial action identified by * the request parameter "actionId" and redirects to the view page for the desired initial action. If no actionId * is passed in the request, we assume that the ID of the initial action is zero. * @return Action.NONE if the desired initial action has a view, otherwise the effect is the same as calling * doExecute() * @throws SystemException * @see #doExecute */ public String doStartWorkflow() throws SystemException { workflow = controller.initializeWorkflow(getInfoGluePrincipal(), getWorkflowName(), actionId, WorkflowController.createWorkflowParameters(ActionContext.getRequest())); return redirectToView(); } /** * Invokes an action in a workflow based on the values of the actionId and workflowId request parameters and * redirects to the action's view if one has been defined. If there is no action view, the user will be taken to * ViewMyDesktopToolStartPage.action, i.e., the effect is the same as calling doExecute(). * <b>NOTE:</b> Assumes there will be one current action with a view; we iterate over the available actions until we * find one with a view, then redirect to that view. If there are other actions with different views, they will be * ignored. * @return NONE if there is an available action with a view and we redirect to the action's view page, otherwise * performs the same operations as doExecute(). * @throws SystemException if an error occurs invoking the action or URL-encoding the action view * @see #doExecute */ public String doInvoke() throws SystemException { logger.info("****************************************"); logger.info("workflowId:" + getWorkflowId()); logger.info("actionId:" + actionId); logger.info("****************************************"); try { if(this.finalReturnAddress != null && !this.finalReturnAddress.equals("")) { logger.info("Final return address get's set to " + this.finalReturnAddress); controller.getPropertySet(getInfoGluePrincipal(), getWorkflowId()).setString("finalReturnAddress", this.finalReturnAddress); } else { String finalReturnAddressCandidate = controller.getPropertySet(getInfoGluePrincipal(), getWorkflowId()).getString("finalReturnAddress"); logger.info("finalReturnAddressCandidate " + finalReturnAddressCandidate); if(finalReturnAddressCandidate != null && !finalReturnAddressCandidate.equals("")) { this.finalReturnAddress = finalReturnAddressCandidate; logger.info("Setting Final return address get's set to " + this.finalReturnAddress + " from properties.."); } } workflow = controller.invokeAction(getInfoGluePrincipal(), getWorkflowId(), actionId, WorkflowController.createWorkflowParameters(ActionContext.getRequest())); return redirectToView(); } catch (InvalidActionException e) { logger.error("An error occurred when invoking an action:" + e.getMessage(), e); return INVALID_ACTION; } catch (WorkflowException e) { throw new SystemException(e); } } /** * Redirects to view defined for the current action(s) in the given workflow. * <b>NOTE:</b> Assumes there will be one current action with a view; we iterate over the available actions until we * find one with a view, then redirect to that view. If there are other actions with different views, they will be * ignored. If we go through the entire list without finding a view, the user will be sent to * ViewMyDesktopToolStartPage.action, i.e., the effect will be the same as calling doExecute(); * @return NONE if there is an available action with a view and we redirect to the action's view page, otherwise * performs the same operations as doExecute(). * @throws SystemException if an error occurs invoking the action or URL-encoding the action view * @see #doExecute * <b>TODO:</b> find a better heuristic for determining which view to go to. */ private String redirectToView() throws SystemException { for (Iterator i = workflow.getAvailableActions().iterator(); i.hasNext();) { String url = getViewUrl((WorkflowActionVO)i.next()); if (url.length() > 0) return redirect(url); } if(this.finalReturnAddress != null && !this.finalReturnAddress.equals("")) return redirect(finalReturnAddress); logger.info("No action view, coming back to mydesktop..."); return doExecute(); } /** * Populates availableWorkflowVOList, workflowVOList, and workflowActionVOList. * @throws SystemException if a workflow error occurs */ private void populateLists() throws SystemException { availableWorkflowVOList = controller.getAvailableWorkflowVOList(getInfoGluePrincipal()); ReflectionComparator workflowComparator = new ReflectionComparator("name"); Collections.sort(availableWorkflowVOList, workflowComparator); final String showAllWorkflows = CmsPropertyHandler.getShowAllWorkflows(); if(showAllWorkflows == null || showAllWorkflows.equalsIgnoreCase("true")) { workflowVOList = controller.getCurrentWorkflowVOList(getInfoGluePrincipal(), 20); } else { workflowVOList = controller.getMyCurrentWorkflowVOList(getInfoGluePrincipal(), 20); } availableShortcutVOList = shortcutController.getAvailableShortcutVOList(getInfoGluePrincipal()); } /** * Builds a list of all available actions for all workflows using the given step filter. Assumes workflowVOList has * been populated. * @return a list of all available actions * @throws NullPointerException if workflowVOList has not been populated */ private List getAvailableActions(StepFilter filter) { List actions = new ArrayList(); for (Iterator workflows = workflowVOList.iterator(); workflows.hasNext();) actions.addAll(((WorkflowVO)workflows.next()).getAvailableActions(filter)); return actions; } /** * Creates the view URL from the given workflow action. * @param action a workflow action * @return the view URL * @throws SystemException if an error occurs while encoding the URL */ private String getViewUrl(WorkflowActionVO action) throws SystemException { if (!action.hasView()) return ""; StringBuffer buffer = new StringBuffer(action.getView()); if (containsQuestionMark(action.getView())) buffer.append('&'); else buffer.append('?'); return buffer.append("workflowId=").append(getWorkflowId()).append("&actionId=").append(action.getId()) .append("&returnAddress=").append(getReturnAddress()).append("&finalReturnAddress=").append(getFinalReturnAddress()).append('&') .append(getRequest().getQueryString()).toString(); } private static boolean containsQuestionMark(String s) { return s.indexOf("?") >= 0; } /** * Returns the return address * @return the return address * @throws SystemException if an error occurs while encoding the URL */ private String getReturnAddress() throws SystemException { try { String cmsFullBaseUrl = CmsPropertyHandler.getCmsFullBaseUrl(); logger.info("cmsFullBaseUrl:" + cmsFullBaseUrl); if(cmsFullBaseUrl != null && !cmsFullBaseUrl.equals("")) return URLEncoder.encode(cmsFullBaseUrl + "/ViewMyDesktopToolStartPage!invoke.action", "UTF-8"); else return URLEncoder.encode(getURLBase() + "/ViewMyDesktopToolStartPage!invoke.action", "UTF-8"); } catch (UnsupportedEncodingException e) { throw new SystemException(e); } } private String redirect(String url) throws SystemException { try { logger.info("Url in doInvoke:" + url); getResponse().sendRedirect(url); return NONE; } catch (IOException e) { throw new SystemException(e); } } /** * Returns the final return address * @return the final return address * @throws SystemException if an error occurs while encoding the URL */ private String getFinalReturnAddress() throws SystemException { try { return URLEncoder.encode(this.finalReturnAddress, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new SystemException(e); } } public void setFinalReturnAddress(String finalReturnAddress) { if(finalReturnAddress != null && !finalReturnAddress.equals("null")) { this.finalReturnAddress = finalReturnAddress; } } private void populateActiveWorkflowVOList() throws SystemException { final String showAllWorkflows = CmsPropertyHandler.getShowAllWorkflows(); if(showAllWorkflows == null || showAllWorkflows.equalsIgnoreCase("true")) { workflowVOList = controller.getCurrentWorkflowVOList(getInfoGluePrincipal(), 20); } else { workflowVOList = controller.getMyCurrentWorkflowVOList(getInfoGluePrincipal(), 20); } } /** * Here are some ajax-methods */ public String doGetActiveWorkflowProperties() throws Exception { StringBuffer sb = new StringBuffer(); String activeWorkflowId = getRequest().getParameter("activeWorkflowId"); populateActiveWorkflowVOList(); List currentWorkflowVOList = workflowVOList; Iterator activeWorkflowVOListIterator = currentWorkflowVOList.iterator(); while(activeWorkflowVOListIterator.hasNext()) { WorkflowVO workflowVO = (WorkflowVO)activeWorkflowVOListIterator.next(); if(activeWorkflowId.equals(workflowVO.getId().toString())) { sb.append("<div id=\"activeWorkflowDetailsProperties\" class=\"propertiesDiv\" style=\"z-index:10\">"); sb.append(" <div id=\"activeWorkflowDetailsPropertiesHandle\" class=\"propertiesDivHandle\">"); sb.append(" <div id=\"propertiesDivLeftHandle\" class=\"propertiesDivLeftHandle\" style=\"width: 300px;\">" + workflowVO.getName() + " - #" + workflowVO.getId() + "</div><div id=\"propertiesDivRightHandle\" class=\"propertiesDivRightHandle\"><a href=\"javascript:hideDiv('activeWorkflowDetailsProperties');\" class=\"white\">close</a></div>"); sb.append(" </div>"); sb.append(" <div id=\"activeWorkflowDetailsPropertiesBody\" class=\"propertiesDivBody\">"); sb.append(" <table border=\"0\" cellpadding=\"4\" cellspacing=\"0\" width=\"100%\">"); Iterator stepsIterator = workflowVO.getSteps().iterator(); while(stepsIterator.hasNext()) { WorkflowStepVO workflowStepVO = (WorkflowStepVO)stepsIterator.next(); sb.append(" <tr>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + workflowStepVO.getName() + "</td>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + (workflowStepVO.getOwner() != null ? workflowStepVO.getOwner() : "Not specified") + "</td>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + (workflowStepVO.getCaller() != null ? workflowStepVO.getCaller() : "Not specified") + "</td>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + workflowStepVO.getStatus() + "</td>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + (workflowStepVO.getStartDate() == null ? "Not started" : formatter.formatDate(workflowStepVO.getStartDate(), "yyyy-MM-dd")) + "</td>"); sb.append(" <td style=\"" + (workflowStepVO.getFinishDate() == null ? "color: black;" : "color: silver;") + "\">" + (workflowStepVO.getFinishDate() == null ? "Not completed" : formatter.formatDate(workflowStepVO.getFinishDate(), "yyyy-MM-dd")) + "</td>"); sb.append(" </tr>"); } sb.append(" </table>"); sb.append(" </div>"); sb.append(" </div>"); break; } } this.getResponse().setContentType("text/plain"); this.getResponse().getWriter().println(sb.toString()); return NONE; } /** * Gets a description div of a workflow */ public String doGetAvailableWorkflowProperties() throws Exception { StringBuffer sb = new StringBuffer(); String workflowName = getRequest().getParameter("workflowName"); try { List availableWorkflowVOList = controller.getAvailableWorkflowVOList(getInfoGluePrincipal()); Iterator availableWorkflowVOListIterator = availableWorkflowVOList.iterator(); while(availableWorkflowVOListIterator.hasNext()) { WorkflowVO availableWorkflowVO = (WorkflowVO)availableWorkflowVOListIterator.next(); if(workflowName.equals(availableWorkflowVO.getName())) { sb.append("<div id=\"availableWorkflowDetailsProperties\" class=\"propertiesDiv\" style=\"z-index: 10;\">"); sb.append(" <div id=\"availableWorkflowDetailsPropertiesHandle\" class=\"propertiesDivHandle\">"); sb.append(" <div id=\"propertiesDivLeftHandle\" class=\"propertiesDivLeftHandle\">" + availableWorkflowVO.getName() + "</div><div id=\"propertiesDivRightHandle\" class=\"propertiesDivRightHandle\"><a href=\"javascript:hideDiv('availableWorkflowDetailsProperties');\" class=\"white\">close</a></div>"); sb.append(" </div>"); sb.append(" <div id=\"availableWorkflowDetailsPropertiesBody\" class=\"propertiesDivBody\">"); sb.append(" <table border=\"0\" cellpadding=\"4\" cellspacing=\"0\" width=\"100%\">"); Iterator workflowStepVOIterator = availableWorkflowVO.getDeclaredSteps().iterator(); while(workflowStepVOIterator.hasNext()) { WorkflowStepVO workflowStepVO = (WorkflowStepVO)workflowStepVOIterator.next(); sb.append(" <tr style=\"background-color: white;\">"); sb.append(" <td>" + workflowStepVO.getName() + "</td>"); sb.append(" <td>" + (workflowStepVO.getOwner() != null ? workflowStepVO.getOwner() : "Not specified") + "</td>"); sb.append(" </tr>"); Iterator workflowActionVOIterator = workflowStepVO.getActions().iterator(); while(workflowActionVOIterator.hasNext()) { WorkflowActionVO workflowActionVO = (WorkflowActionVO)workflowActionVOIterator.next(); sb.append("<tr style=\"background-color: #eeeeee;\">"); sb.append(" <td style=\"padding-left: 20px; font-size:10px;\">" + workflowActionVO.getName() + "</td>"); sb.append(" <td style=\"padding-left: 20px; font-size:10px;\"><!--" + workflowActionVO.getView() + "--></td>"); sb.append(" </tr>"); } } sb.append(" </table>"); sb.append(" </div>"); sb.append("</div>"); } } } catch (Exception e) { e.printStackTrace(); } this.getResponse().setContentType("text/plain"); this.getResponse().getWriter().println(sb.toString()); return NONE; } }