package org.alfresco.web.bean.actions; import java.io.Serializable; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.application.ViewHandler; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.faces.model.SelectItem; import org.alfresco.config.Config; import org.alfresco.config.ConfigElement; import org.alfresco.config.ConfigService; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.bean.TemplateSupportBean; import org.alfresco.web.bean.actions.handlers.MailHandler; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.bean.wizard.BaseWizardBean; import org.alfresco.web.data.IDataContainer; import org.alfresco.web.data.QuickSort; import org.alfresco.web.ui.common.Utils; import org.alfresco.web.ui.common.component.UIGenericPicker; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Base class for the "Run Action" and "Create Rule" wizards. * * @author gavinc */ public abstract class BaseActionWizard extends BaseWizardBean { protected static final String PROP_ACTION_NAME = "actionName"; protected static final String PROP_ACTION_SUMMARY = "actionSummary"; protected static final String NO_PARAMS_MARKER = "noParamsMarker"; protected ActionService actionService; protected MimetypeService mimetypeService; protected PersonService personService; protected AuthorityService authorityService; protected List actions; protected List transformers; protected List imageTransformers; protected List aspects; protected List users; protected List encodings; protected List objectTypes; protected List emailRecipients; protected DataModel allActionsDataModel; protected DataModel emailRecipientsDataModel; protected boolean editingAction; protected String action; protected String usingTemplate = null; protected String returnViewId = null; protected Map currentActionProperties; protected List> allActionsProperties; protected Map actionHandlers; private static final Log logger = LogFactory.getLog(BaseActionWizard.class); // ------------------------------------------------------------------------------ // Wizard implementation @Override public void init(Map parameters) { super.init(parameters); this.action = null; this.users = null; this.actions = null; this.emailRecipientsDataModel = null; this.usingTemplate = null; this.emailRecipients = new ArrayList(4); this.allActionsProperties = new ArrayList>(); this.currentActionProperties = new HashMap(3); initialiseActionHandlers(); } // ------------------------------------------------------------------------------ // Bean Getters and Setters /** * @return Returns the selected action */ public String getAction() { return this.action; } /** * @param action Sets the selected action */ public void setAction(String action) { this.action = action; } /** * @return Returns if a template has been inserted by a user for email body. */ public String getUsingTemplate() { return this.usingTemplate; } /** * @param usingTemplate Template that has been inserted by a user for the email body. */ public void setUsingTemplate(String usingTemplate) { this.usingTemplate = usingTemplate; } /** * Returns the properties for all the actions as a JSF DataModel * * @return JSF DataModel representing the action properties */ public DataModel getAllActionsDataModel() { if (this.allActionsDataModel == null) { this.allActionsDataModel = new ListDataModel(); } this.allActionsDataModel.setWrappedData(this.allActionsProperties); return this.allActionsDataModel; } /** * Returns the properties for email recipients JSF DataModel * * @return JSF DataModel wrapping the current email recipients */ public DataModel getEmailRecipientsDataModel() { if (this.emailRecipientsDataModel == null) { this.emailRecipientsDataModel = new ListDataModel(); } this.emailRecipientsDataModel.setWrappedData(this.emailRecipients); return this.emailRecipientsDataModel; } /** * @return Gets the action settings */ public Map getActionProperties() { return this.currentActionProperties; } /** * @return Returns the list of selectable actions */ public List getActions() { if (this.actions == null) { List ruleActions = this.actionService.getActionDefinitions(); this.actions = new ArrayList(); for (ActionDefinition ruleActionDef : ruleActions) { this.actions.add(new SelectItem(ruleActionDef.getName(), ruleActionDef.getTitle())); } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.actions, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); // add the select an action item at the start of the list this.actions.add(0, new SelectItem("null", Application.getMessage(FacesContext.getCurrentInstance(), "select_an_action"))); } return this.actions; } /** * Returns the aspects that are available * * @return List of SelectItem objects representing the available aspects */ public List getAspects() { if (this.aspects == null) { ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); Config wizardCfg = svc.getConfig("Action Wizards"); if (wizardCfg != null) { ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects"); if (aspectsCfg != null) { FacesContext context = FacesContext.getCurrentInstance(); this.aspects = new ArrayList(); for (ConfigElement child : aspectsCfg.getChildren()) { QName idQName = Repository.resolveToQName(child.getAttribute("name")); if (idQName != null) { // try and get the display label from config String label = Utils.getDisplayLabel(context, child); // if there wasn't a client based label try and get it from the dictionary if (label == null) { AspectDefinition aspectDef = this.dictionaryService.getAspect(idQName); if (aspectDef != null) { label = aspectDef.getTitle(); } else { label = idQName.getLocalName(); } } this.aspects.add(new SelectItem(idQName.toString(), label)); } else { logger.warn("Failed to resolve aspect '" + child.getAttribute("name") + "'"); } } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.aspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } else { logger.warn("Could not find 'aspects' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } } return this.aspects; } /** * @return Returns a list of object types to allow the user to select from */ public List getObjectTypes() { if (this.objectTypes == null) { FacesContext context = FacesContext.getCurrentInstance(); // add the well known object type to start with this.objectTypes = new ArrayList(5); // add any configured content or folder sub-types to the list ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); Config wizardCfg = svc.getConfig("Action Wizards"); if (wizardCfg != null) { ConfigElement typesCfg = wizardCfg.getConfigElement("specialise-types"); if (typesCfg != null) { for (ConfigElement child : typesCfg.getChildren()) { QName idQName = Repository.resolveToQName(child.getAttribute("name")); TypeDefinition typeDef = this.dictionaryService.getType(idQName); // make sure the type is a subtype of content or folder but not // the content or folder type itself if (typeDef != null && typeDef.getName().equals(ContentModel.TYPE_CONTENT) == false && typeDef.getName().equals(ContentModel.TYPE_FOLDER) == false && (this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_CONTENT) || this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_FOLDER))) { // try and get the display label from config String label = Utils.getDisplayLabel(context, child); // if there wasn't a client based label try and get it from the dictionary if (label == null) { label = typeDef.getTitle(); } // finally, just use the localname if (label == null) { label = idQName.getLocalName(); } this.objectTypes.add(new SelectItem(idQName.toString(), label)); } } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.objectTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); // add the select an action item at the start of the list this.objectTypes.add(0, new SelectItem("null", Application.getMessage(FacesContext.getCurrentInstance(), "select_a_type"))); } else { logger.warn("Could not find 'specialise-types' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } } return this.objectTypes; } /** * @return the List of users in the system wrapped in SelectItem objects */ public List getUsers() { if (this.users == null) { List userNodes = Repository.getUsers( FacesContext.getCurrentInstance(), this.nodeService, this.searchService); this.users = new ArrayList(); for (Node user : userNodes) { String email = (String)user.getProperties().get("email"); if (email != null && email.length() > 0) { this.users.add(new SelectItem(email, (String)user.getProperties().get("fullName"))); } } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.users, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } return this.users; } /** * Returns the transformers that are available * * @return List of SelectItem objects representing the available transformers */ public List getTransformers() { if (this.transformers == null) { ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); Config wizardCfg = svc.getConfig("Action Wizards"); if (wizardCfg != null) { ConfigElement transformersCfg = wizardCfg.getConfigElement("transformers"); if (transformersCfg != null) { FacesContext context = FacesContext.getCurrentInstance(); Map mimeTypes = this.mimetypeService.getDisplaysByMimetype(); this.transformers = new ArrayList(); for (ConfigElement child : transformersCfg.getChildren()) { String id = child.getAttribute("name"); // try and get the display label from config String label = Utils.getDisplayLabel(context, child); // if there wasn't a client based label get it from the mime type service if (label == null) { label = mimeTypes.get(id); } this.transformers.add(new SelectItem(id, label)); } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.transformers, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } else { logger.warn("Could not find 'transformers' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } } return this.transformers; } /** * Returns the image transformers that are available * * @return List of SelectItem objects representing the available image transformers */ public List getImageTransformers() { if (this.imageTransformers == null) { ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance()); Config wizardCfg = svc.getConfig("Action Wizards"); if (wizardCfg != null) { ConfigElement transformersCfg = wizardCfg.getConfigElement("image-transformers"); if (transformersCfg != null) { FacesContext context = FacesContext.getCurrentInstance(); Map mimeTypes = this.mimetypeService.getDisplaysByMimetype(); this.imageTransformers = new ArrayList(); for (ConfigElement child : transformersCfg.getChildren()) { String id = child.getAttribute("name"); // try and get the display label from config String label = Utils.getDisplayLabel(context, child); // if there wasn't a client based label get it from the mime type service if (label == null) { label = mimeTypes.get(id); } this.imageTransformers.add(new SelectItem(id, label)); } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.imageTransformers, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } else { logger.warn("Could not find 'image-transformers' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } } return this.imageTransformers; } /** * Returns the current list of email recipients * * @return List of email recipients */ public List getEmailRecipients() { return this.emailRecipients; } // ------------------------------------------------------------------------------ // Action event handlers /** * Displays the settings page for the current action being added */ public void promptForActionValues() { // set the flag to show we are creating a new action this.editingAction = false; FacesContext context = FacesContext.getCurrentInstance(); this.returnViewId = context.getViewRoot().getViewId(); String viewId = null; HashMap actionProps = new HashMap(3); actionProps.put(PROP_ACTION_NAME, this.action); this.currentActionProperties = actionProps; // get the handler for the action, if there isn't one we presume it // is a no-parameter action IHandler handler = this.actionHandlers.get(this.action); if (handler != null) { // setup any UI defaults the action may have and get the location of // the JSP used to collect the parameters handler.setupUIDefaults(actionProps); viewId = handler.getJSPPath(); } else { // just add the action to the list and use the title as the summary ActionDefinition actionDef = this.actionService.getActionDefinition(this.action); actionProps.put(PROP_ACTION_SUMMARY, actionDef.getTitle()); // add the no params marker so we can disable the edit action actionProps.put(NO_PARAMS_MARKER, "no-params"); this.allActionsProperties.add(actionProps); // come back to the same page we're on now as there are no params to collect viewId = this.returnViewId; } if (logger.isDebugEnabled()) logger.debug("Added '" + this.action + "' action to list"); // go to the page to collect the settings goToPage(context, viewId); } /** * Sets up the context for editing existing action values */ @SuppressWarnings("unchecked") public void editAction() { // use the built in JSF support for retrieving the object for the // row that was clicked by the user Map actionToEdit = (Map)this.allActionsDataModel.getRowData(); this.action = (String)actionToEdit.get(PROP_ACTION_NAME); this.currentActionProperties = actionToEdit; // set the flag to show we are editing an action this.editingAction = true; // remember the page we're on FacesContext context = FacesContext.getCurrentInstance(); this.returnViewId = context.getViewRoot().getViewId(); // go to the action page (as there is an edit option visible, // there must be a handler for the action so we don't check) goToPage(context, this.actionHandlers.get(this.action).getJSPPath()); } /** * Adds the action just setup by the user to the list of actions for the rule */ public void addAction() { FacesContext context = FacesContext.getCurrentInstance(); // this is called from the actions page so there must be a handler // present so there's no need to check for null String summary = this.actionHandlers.get(this.action).generateSummary( context, this, this.currentActionProperties); if (summary != null) { this.currentActionProperties.put(PROP_ACTION_SUMMARY, summary); } if (this.editingAction == false) { this.allActionsProperties.add(this.currentActionProperties); } // reset the action drop down this.action = null; // refresh the wizard goToPage(context, this.returnViewId); } /** * Removes the requested action from the list */ public void removeAction() { // use the built in JSF support for retrieving the object for the // row that was clicked by the user Map actionToRemove = (Map)this.allActionsDataModel.getRowData(); this.allActionsProperties.remove(actionToRemove); // reset the action drop down this.action = null; // refresh the wizard FacesContext context = FacesContext.getCurrentInstance(); goToPage(context, context.getViewRoot().getViewId()); } /** * Cancels the addition of the action */ public void cancelAddAction() { if (this.editingAction == false) { this.currentActionProperties.clear(); } // reset the action drop down this.action = null; // refresh the wizard goToPage(FacesContext.getCurrentInstance(), this.returnViewId); } /** * Action handler called when the Add button is pressed to add an email recipient */ public void addRecipient(ActionEvent event) { UIGenericPicker picker = (UIGenericPicker)event.getComponent(); String[] results = picker.getSelectedResults(); if (results != null && results.length != 0) { for (String authority : results) { // first check the authority has not already been added to the list boolean alreadyAdded = false; for (int i=0; i(20); // instantiate each handler and store in the map for (ConfigElement child : actionHandlerCfg.getChildren()) { String actionName = child.getAttribute("name"); String handlerClass = child.getAttribute("class"); if (actionName != null && actionName.length() > 0 && handlerClass != null && handlerClass.length() > 0) { try { Class klass = Class.forName(handlerClass); IHandler handler = (IHandler)klass.newInstance(); this.actionHandlers.put(actionName, handler); } catch (Exception e) { throw new AlfrescoRuntimeException("Failed to setup action handler for '" + actionName + "'", e); } } } } else { logger.warn("Could not find 'action-handlers' configuration element"); } } else { logger.warn("Could not find 'Action Wizards' configuration section"); } } } // ------------------------------------------------------------------------------ // Inner classes /** * Simple wrapper class for email recipient fields */ public static class RecipientWrapper { public RecipientWrapper(String name, String authority) { this.name = name; this.authority = authority; } public String getName() { return this.name; } public String getAuthority() { return this.authority; } public boolean equals(Object obj) { if (obj instanceof RecipientWrapper) { return this.authority.equals( ((RecipientWrapper)obj).getAuthority() ); } else { return false; } } public int hashCode() { return authority.hashCode(); } private String name; private String authority; } }