properties)
+ {
+ String taskId = (String)properties.get(PROP_TASK_ID);
+ if (taskId == null)
+ {
+ throw new IllegalArgumentException("Unable to execute EndTaskCommand - mandatory parameter not supplied: " + PROP_TASK_ID);
+ }
+ String transition = (String)properties.get(PROP_TRANSITION);
+
+ // end task
+ WorkflowService workflowService = serviceRegistry.getWorkflowService();
+ return workflowService.endTask(taskId, transition);
+ }
+}
diff --git a/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java b/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java
new file mode 100644
index 0000000000..4c0323a6fa
--- /dev/null
+++ b/source/java/org/alfresco/web/app/servlet/command/TaskCommandProcessor.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.app.servlet.command;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.service.ServiceRegistry;
+
+/**
+ * Task specific command processor implementation.
+ *
+ * Responsible for executing workflow task operations.
+ *
+ * @author David Caruana
+ */
+public final class TaskCommandProcessor implements CommandProcessor
+{
+ private String taskId;
+ private String transition = null;
+ private String command;
+
+ static
+ {
+ // add our commands to the command registry
+ CommandFactory.getInstance().registerCommand("end", EndTaskCommand.class);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.web.app.servlet.command.CommandProcessor#validateArguments(javax.servlet.ServletContext, java.lang.String, java.util.Map, java.lang.String[])
+ */
+ public boolean validateArguments(ServletContext sc, String command, Map args, String[] urlElements)
+ {
+ if (urlElements.length == 0)
+ {
+ throw new IllegalArgumentException("Not enough URL arguments passed to command servlet.");
+ }
+ taskId = urlElements[0];
+ if (urlElements.length == 2)
+ {
+ transition = urlElements[1];
+ }
+ return true;
+ }
+
+ /**
+ * @see org.alfresco.web.app.servlet.command.CommandProcessor#process(org.alfresco.service.ServiceRegistry, javax.servlet.http.HttpServletRequest, java.lang.String)
+ */
+ public void process(ServiceRegistry serviceRegistry, HttpServletRequest request, String command)
+ {
+ Map properties = new HashMap(1, 1.0f);
+ // all workflow commands use a "target" Node property as an argument
+ properties.put(EndTaskCommand.PROP_TASK_ID, taskId);
+ if (transition != null)
+ {
+ properties.put(EndTaskCommand.PROP_TRANSITION, transition);
+ }
+ Command cmd = CommandFactory.getInstance().createCommand(command);
+ if (cmd == null)
+ {
+ throw new AlfrescoRuntimeException("Unregistered workflow command specified: " + command);
+ }
+ cmd.execute(serviceRegistry, properties);
+ this.command = command;
+ }
+
+ /**
+ * @see org.alfresco.web.app.servlet.command.CommandProcessor#outputStatus(java.io.PrintWriter)
+ */
+ public void outputStatus(PrintWriter out)
+ {
+ out.print("Task command: '");
+ out.print(command);
+ out.print("' executed against task: ");
+ out.println(taskId);
+ }
+
+}
diff --git a/source/java/org/alfresco/web/bean/AboutBean.java b/source/java/org/alfresco/web/bean/AboutBean.java
new file mode 100644
index 0000000000..83f9d26916
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/AboutBean.java
@@ -0,0 +1,43 @@
+package org.alfresco.web.bean;
+
+import org.alfresco.service.descriptor.DescriptorService;
+
+/**
+ * Simple backing bean used by the about page to display the version.
+ *
+ * @author gavinc
+ */
+public class AboutBean
+{
+ DescriptorService descriptorService;
+
+ /**
+ * Retrieves the version of the repository.
+ *
+ * @return The version string
+ */
+ public String getVersion()
+ {
+ return this.descriptorService.getServerDescriptor().getVersion();
+ }
+
+ /**
+ * Retrieves the edition of the repository.
+ *
+ * @return The edition
+ */
+ public String getEdition()
+ {
+ return this.descriptorService.getServerDescriptor().getEdition();
+ }
+
+ /**
+ * Sets the DescriptorService.
+ *
+ * @param descriptorService The DescriptorService
+ */
+ public void setDescriptorService(DescriptorService descriptorService)
+ {
+ this.descriptorService = descriptorService;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java
index 05f2451530..9ee2208ae1 100644
--- a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java
+++ b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java
@@ -57,6 +57,7 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.CachingDateFormat;
+import org.alfresco.util.ISO9075;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.SearchContext.RangeProperties;
import org.alfresco.web.bean.repository.MapNode;
@@ -620,10 +621,6 @@ public class AdvancedSearchBean
}
}
}
-
- // make sure the list is sorted by the label
- QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
- sorter.sort();
}
}
@@ -672,10 +669,6 @@ public class AdvancedSearchBean
}
}
}
-
- // make sure the list is sorted by the label
- QuickSort sorter = new QuickSort(this.folderTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
- sorter.sort();
}
}
@@ -1375,7 +1368,8 @@ public class AdvancedSearchBean
{
FacesContext fc = FacesContext.getCurrentInstance();
User user = Application.getCurrentUser(fc);
- String xpath = NamespaceService.APP_MODEL_PREFIX + ":" + QName.createValidLocalName(user.getUserName());
+ String userName = ISO9075.encode(user.getUserName());
+ String xpath = NamespaceService.APP_MODEL_PREFIX + ":" + QName.createValidLocalName(userName);
List results = null;
try
diff --git a/source/java/org/alfresco/web/bean/BaseDetailsBean.java b/source/java/org/alfresco/web/bean/BaseDetailsBean.java
index 65795a5cce..763b1915a3 100644
--- a/source/java/org/alfresco/web/bean/BaseDetailsBean.java
+++ b/source/java/org/alfresco/web/bean/BaseDetailsBean.java
@@ -256,7 +256,7 @@ public abstract class BaseDetailsBean
/**
* Action handler to apply the selected Template and Templatable aspect to the current Space
*/
- public String applyTemplate()
+ public void applyTemplate(ActionEvent event)
{
if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false)
{
@@ -283,13 +283,12 @@ public abstract class BaseDetailsBean
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
}
- return getReturnOutcome();
}
/**
* Action handler to remove a dashboard template from the current Space
*/
- public String removeTemplate()
+ public void removeTemplate(ActionEvent event)
{
try
{
@@ -305,14 +304,8 @@ public abstract class BaseDetailsBean
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
- return getReturnOutcome();
}
- /**
- * @return return to details page JSF navigation outcome
- */
- protected abstract String getReturnOutcome();
-
/**
* Action Handler to take Ownership of the current Space
*/
diff --git a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java
index 7db8022d9d..b130c3d788 100644
--- a/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java
+++ b/source/java/org/alfresco/web/bean/CheckinCheckoutBean.java
@@ -759,7 +759,8 @@ public class CheckinCheckoutBean
{
try
{
- tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
+ FacesContext context = FacesContext.getCurrentInstance();
+ tx = Repository.getUserTransaction(context);
tx.begin();
if (LOGGER.isDebugEnabled())
@@ -778,8 +779,10 @@ public class CheckinCheckoutBean
{
// add the content to an anonymous but permanent writer location
// we can then retrieve the URL to the content to to be set on the node during checkin
- ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, false);
- // TODO: Adjust the mimetype
+ ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
+ // also update the mime type in case a different type of file is uploaded
+ String mimeType = Repository.getMimeTypeForFileName(context, this.fileName);
+ writer.setMimetype(mimeType);
writer.putContent(this.file);
contentUrl = writer.getContentUrl();
}
@@ -846,7 +849,8 @@ public class CheckinCheckoutBean
{
try
{
- tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
+ FacesContext context = FacesContext.getCurrentInstance();
+ tx = Repository.getUserTransaction(context);
tx.begin();
if (LOGGER.isDebugEnabled())
@@ -854,7 +858,12 @@ public class CheckinCheckoutBean
// get an updating writer that we can use to modify the content on the current node
ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
- writer.putContent(this.file);
+
+ // also update the mime type in case a different type of file is uploaded
+ String mimeType = Repository.getMimeTypeForFileName(context, this.fileName);
+ writer.setMimetype(mimeType);
+
+ writer.putContent(this.file);
// commit the transaction
tx.commit();
diff --git a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java
index f09af0d416..6d8c5eba3a 100644
--- a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java
+++ b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java
@@ -243,8 +243,8 @@ public class DocumentDetailsBean extends BaseDetailsBean
{
// we know for now that the general classifiable aspect only will be
// applied so we can retrive the categories property direclty
- Collection categories = (Collection)this.nodeService.getProperty(getDocument().getNodeRef(),
- ContentModel.PROP_CATEGORIES);
+ Collection categories = (Collection)this.nodeService.getProperty(
+ getDocument().getNodeRef(), ContentModel.PROP_CATEGORIES);
if (categories == null || categories.size() == 0)
{
@@ -256,16 +256,13 @@ public class DocumentDetailsBean extends BaseDetailsBean
MSG_HAS_FOLLOWING_CATEGORIES));
builder.append("");
- for (Object obj : categories)
+ for (NodeRef ref : categories)
{
- if (obj instanceof NodeRef)
+ if (this.nodeService.exists(ref))
{
- if (this.nodeService.exists((NodeRef)obj))
- {
- builder.append("");
- builder.append(Repository.getNameForNode(this.nodeService, (NodeRef)obj));
- builder.append(" ");
- }
+ builder.append("");
+ builder.append(Repository.getNameForNode(this.nodeService, ref));
+ builder.append(" ");
}
}
builder.append(" ");
@@ -992,14 +989,6 @@ public class DocumentDetailsBean extends BaseDetailsBean
{
return "document-props";
}
-
- /**
- * @see org.alfresco.web.bean.BaseDetailsBean#getReturnOutcome()
- */
- protected String getReturnOutcome()
- {
- return OUTCOME_RETURN;
- }
/**
* Returns a model for use by a template on the Document Details page.
diff --git a/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java b/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java
index 27c6cd3ffc..eedbc2560b 100644
--- a/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java
+++ b/source/java/org/alfresco/web/bean/DocumentPropertiesBean.java
@@ -262,7 +262,7 @@ public class DocumentPropertiesBean
String statusMsg = MessageFormat.format(
Application.getMessage(
FacesContext.getCurrentInstance(), "error_exists"),
- e.getExisting().getName());
+ e.getName());
Utils.addErrorMessage(statusMsg);
// no outcome
outcome = null;
diff --git a/source/java/org/alfresco/web/bean/ErrorBean.java b/source/java/org/alfresco/web/bean/ErrorBean.java
index 6a387f2c36..8cf9b29244 100644
--- a/source/java/org/alfresco/web/bean/ErrorBean.java
+++ b/source/java/org/alfresco/web/bean/ErrorBean.java
@@ -58,11 +58,22 @@ public class ErrorBean
}
/**
- * @param lastError The lastError to set.
+ * @param error The lastError to set.
*/
- public void setLastError(Throwable lastError)
+ public void setLastError(Throwable error)
{
- this.lastError = lastError;
+ // servlet exceptions hide the actual error within the rootCause
+ // variable, set the base error to that and throw away the
+ // ServletException wrapper
+ if (error instanceof ServletException &&
+ ((ServletException)error).getRootCause() != null)
+ {
+ this.lastError = ((ServletException)error).getRootCause();
+ }
+ else
+ {
+ this.lastError = error;
+ }
}
/**
@@ -74,29 +85,17 @@ public class ErrorBean
if (this.lastError != null)
{
- StringBuilder builder = null;
- Throwable cause = null;
- if (this.lastError instanceof ServletException &&
- ((ServletException)this.lastError).getRootCause() != null)
- {
- // servlet exception puts the actual error in root cause!!
- Throwable actualError = ((ServletException)this.lastError).getRootCause();
- builder = new StringBuilder(actualError.toString());
- cause = actualError.getCause();
- }
- else
- {
- builder = new StringBuilder(this.lastError.toString());
- cause = this.lastError.getCause();
- }
+ StringBuilder builder = new StringBuilder(this.lastError.toString());;
+ Throwable cause = this.lastError.getCause();
+ // build up stack trace of all causes
while (cause != null)
{
- builder.append(" caused by: ");
+ builder.append("\ncaused by:\n");
builder.append(cause.toString());
if (cause instanceof ServletException &&
- ((ServletException)cause).getRootCause() != null)
+ ((ServletException)cause).getRootCause() != null)
{
cause = ((ServletException)cause).getRootCause();
}
@@ -107,6 +106,11 @@ public class ErrorBean
}
message = builder.toString();
+
+ // format the message for HTML display
+ message = message.replaceAll("<", "<");
+ message = message.replaceAll(">", ">");
+ message = message.replaceAll("\n", " ");
}
return message;
@@ -119,18 +123,14 @@ public class ErrorBean
{
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
+ this.lastError.printStackTrace(writer);
- if (this.lastError instanceof ServletException &&
- ((ServletException)this.lastError).getRootCause() != null)
- {
- Throwable actualError = ((ServletException)this.lastError).getRootCause();
- actualError.printStackTrace(writer);
- }
- else
- {
- this.lastError.printStackTrace(writer);
- }
+ // format the message for HTML display
+ String trace = stringWriter.toString();
+ trace = trace.replaceAll("<", "<");
+ trace = trace.replaceAll(">", ">");
+ trace = trace.replaceAll("\n", " ");
- return stringWriter.toString().replaceAll("\r\n", " ");
+ return trace;
}
}
diff --git a/source/java/org/alfresco/web/bean/LoginBean.java b/source/java/org/alfresco/web/bean/LoginBean.java
index 896d5c117d..17274e42b9 100644
--- a/source/java/org/alfresco/web/bean/LoginBean.java
+++ b/source/java/org/alfresco/web/bean/LoginBean.java
@@ -279,12 +279,15 @@ public class LoginBean
// in the session - this is used by the servlet filter etc. on each page to check for login
this.authenticationService.authenticate(this.username, this.password.toCharArray());
+ // Set the user name as stored by the back end
+ this.username = this.authenticationService.getCurrentUserName();
+
// remove the session invalidated flag (used to remove last username cookie by AuthenticationFilter)
session.remove(AuthenticationHelper.SESSION_INVALIDATED);
// setup User object and Home space ID
User user = new User(
- this.authenticationService.getCurrentUserName(),
+ this.username,
this.authenticationService.getCurrentTicket(),
personService.getPerson(this.username));
@@ -325,7 +328,17 @@ public class LoginBean
}
else
{
- return "success";
+ // special case to handle jump to My Alfresco page initially
+ String location = Application.getClientConfig(FacesContext.getCurrentInstance()).getInitialLocation();
+ if (NavigationBean.LOCATION_MYALFRESCO.equals(location))
+ {
+ return "myalfresco";
+ }
+ else
+ {
+ // generally this will navigate to the generic browse screen
+ return "success";
+ }
}
}
catch (AuthenticationException aerr)
diff --git a/source/java/org/alfresco/web/bean/NavigationBean.java b/source/java/org/alfresco/web/bean/NavigationBean.java
index 57a7f07158..d426a07732 100644
--- a/source/java/org/alfresco/web/bean/NavigationBean.java
+++ b/source/java/org/alfresco/web/bean/NavigationBean.java
@@ -187,11 +187,93 @@ public class NavigationBean
}
/**
- * @param toolbarLocation The toolbar Location to set.
+ * @param location The toolbar Location to set.
*/
- public void setToolbarLocation(String toolbarLocation)
+ public void setToolbarLocation(String location)
{
- this.toolbarLocation = toolbarLocation;
+ processToolbarLocation(location, true);
+ }
+
+ /**
+ * Process the selected toolbar location. Setup the breadcrumb with initial value and
+ * setup the current node ID. This method can also perform the navigatin setup if requested.
+ *
+ * @param location Toolbar location constant
+ * @param navigate True to perform navigation, false otherwise
+ */
+ private void processToolbarLocation(String location, boolean navigate)
+ {
+ this.toolbarLocation = location;
+
+ FacesContext context = FacesContext.getCurrentInstance();
+ if (LOCATION_COMPANY.equals(location))
+ {
+ List elements = new ArrayList(1);
+ Node companyHome = getCompanyHomeNode();
+ elements.add(new NavigationBreadcrumbHandler(companyHome.getNodeRef(), companyHome.getName()));
+ setLocation(elements);
+ setCurrentNodeId(companyHome.getId());
+
+ // we need to force a navigation to refresh the browse screen breadcrumb
+ if (navigate)
+ {
+ context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
+ }
+ }
+ else if (LOCATION_HOME.equals(location))
+ {
+ List elements = new ArrayList(1);
+ String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId();
+ NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId);
+ String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef);
+ elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName));
+ setLocation(elements);
+ setCurrentNodeId(homeSpaceRef.getId());
+
+ // we need to force a navigation to refresh the browse screen breadcrumb
+ if (navigate)
+ {
+ context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
+ }
+ }
+ else if (LOCATION_GUEST.equals(location))
+ {
+ List elements = new ArrayList(1);
+ Node guestHome = getGuestHomeNode();
+ elements.add(new NavigationBreadcrumbHandler(guestHome.getNodeRef(), guestHome.getName()));
+ setLocation(elements);
+ setCurrentNodeId(guestHome.getId());
+
+ // we need to force a navigation to refresh the browse screen breadcrumb
+ if (navigate)
+ {
+ context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
+ }
+ }
+ else if (LOCATION_MYALFRESCO.equals(location))
+ {
+ List elements = new ArrayList(1);
+ elements.add(new IBreadcrumbHandler()
+ {
+ public String navigationOutcome(UIBreadcrumb breadcrumb)
+ {
+ setLocation( (List)breadcrumb.getValue() );
+ return OUTCOME_MYALFRESCO;
+ };
+
+ public String toString()
+ {
+ return Application.getMessage(FacesContext.getCurrentInstance(), MSG_MYALFRESCO);
+ };
+ });
+ setLocation(elements);
+
+ // we need to force a navigation to refresh the browse screen breadcrumb
+ if (navigate)
+ {
+ context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_MYALFRESCO);
+ }
+ }
}
/**
@@ -396,20 +478,20 @@ public class NavigationBean
if (diskShare != null)
{
- ContentContext contentCtx = (ContentContext) diskShare.getContext();
- NodeRef rootNode = contentCtx.getRootNode();
- try
- {
- String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare));
-
- node.getProperties().put("cifsPath", cifsPath);
- node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part
- }
- catch(AccessDeniedException ade)
- {
- node.getProperties().put("cifsPath", "");
- node.getProperties().put("cifsPathLabel",""); // strip file:/// part
- }
+ ContentContext contentCtx = (ContentContext) diskShare.getContext();
+ NodeRef rootNode = contentCtx.getRootNode();
+ try
+ {
+ String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare));
+
+ node.getProperties().put("cifsPath", cifsPath);
+ node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part
+ }
+ catch(AccessDeniedException ade)
+ {
+ node.getProperties().put("cifsPath", "");
+ node.getProperties().put("cifsPathLabel",""); // strip file:/// part
+ }
}
this.currentNode = node;
@@ -425,26 +507,13 @@ public class NavigationBean
{
if (this.location == null)
{
- // set the current node to the users Home Space Id if one has not already been set
- NodeRef homeSpaceRef;
- List elements = new ArrayList(1);
- if (getCurrentNodeId() == null)
+ // get the initial location from the client config
+ String initialLocation = clientConfig.getInitialLocation();
+ if (initialLocation == null || initialLocation.length() == 0)
{
- User user = Application.getCurrentUser(FacesContext.getCurrentInstance());
- homeSpaceRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
+ initialLocation = LOCATION_HOME;
}
- else
- {
- homeSpaceRef = new NodeRef(Repository.getStoreRef(), getCurrentNodeId());
- }
-
- // set initial node ID
- setCurrentNodeId(homeSpaceRef.getId());
-
- // setup the breadcrumb with the same initial location
- String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef);
- elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName));
- setLocation(elements);
+ processToolbarLocation(initialLocation, false);
}
return this.location;
@@ -591,63 +660,6 @@ public class NavigationBean
UIModeList locationList = (UIModeList)event.getComponent();
String location = locationList.getValue().toString();
setToolbarLocation(location);
-
- if (LOCATION_COMPANY.equals(location))
- {
- List elements = new ArrayList(1);
- Node companyHome = getCompanyHomeNode();
- elements.add(new NavigationBreadcrumbHandler(companyHome.getNodeRef(), companyHome.getName()));
- setLocation(elements);
- setCurrentNodeId(companyHome.getId());
-
- // we need to force a navigation to refresh the browse screen breadcrumb
- context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
- }
- else if (LOCATION_HOME.equals(location))
- {
- List elements = new ArrayList(1);
- String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId();
- NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId);
- String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef);
- elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName));
- setLocation(elements);
- setCurrentNodeId(homeSpaceRef.getId());
-
- // we need to force a navigation to refresh the browse screen breadcrumb
- context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
- }
- else if (LOCATION_GUEST.equals(location))
- {
- List elements = new ArrayList(1);
- Node guestHome = getGuestHomeNode();
- elements.add(new NavigationBreadcrumbHandler(guestHome.getNodeRef(), guestHome.getName()));
- setLocation(elements);
- setCurrentNodeId(guestHome.getId());
-
- // we need to force a navigation to refresh the browse screen breadcrumb
- context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_BROWSE);
- }
- else if (LOCATION_DASHBOARD.equals(location))
- {
- List elements = new ArrayList(1);
- elements.add(new IBreadcrumbHandler()
- {
- public String navigationOutcome(UIBreadcrumb breadcrumb)
- {
- setLocation( (List)breadcrumb.getValue() );
- return OUTCOME_MYALFRESCO;
- };
-
- public String toString()
- {
- return Application.getMessage(FacesContext.getCurrentInstance(), MSG_MYALFRESCO);
- };
- });
- setLocation(elements);
-
- // we need to force a navigation to refresh the browse screen breadcrumb
- context.getApplication().getNavigationHandler().handleNavigation(context, null, OUTCOME_MYALFRESCO);
- }
}
catch (InvalidNodeRefException refErr)
{
@@ -686,6 +698,22 @@ public class NavigationBean
return this.cifsServerPath;
}
+ /**
+ * @return true if the current space has an RSS feed applied
+ */
+ public boolean isRSSFeed()
+ {
+ return SpaceDetailsBean.hasRSSFeed(getCurrentNode());
+ }
+
+ /**
+ * @return RSS Feed URL for the current space
+ */
+ public String getRSSFeedURL()
+ {
+ return SpaceDetailsBean.buildRSSFeedURL(getCurrentNode());
+ }
+
// ------------------------------------------------------------------------------
// Private helpers
@@ -770,10 +798,10 @@ public class NavigationBean
private static Logger s_logger = Logger.getLogger(NavigationBean.class);
/** constant values used by the toolbar location modelist control */
- private static final String LOCATION_COMPANY = "company";
- private static final String LOCATION_HOME = "home";
- private static final String LOCATION_GUEST = "guest";
- private static final String LOCATION_DASHBOARD = "dashboard";
+ static final String LOCATION_COMPANY = "companyhome";
+ static final String LOCATION_HOME = "userhome";
+ static final String LOCATION_GUEST = "guesthome";
+ static final String LOCATION_MYALFRESCO = "myalfresco";
private static final String MSG_MYALFRESCO = "my_alfresco";
diff --git a/source/java/org/alfresco/web/bean/SpaceDetailsBean.java b/source/java/org/alfresco/web/bean/SpaceDetailsBean.java
index d55818b869..234a37ecdb 100644
--- a/source/java/org/alfresco/web/bean/SpaceDetailsBean.java
+++ b/source/java/org/alfresco/web/bean/SpaceDetailsBean.java
@@ -34,6 +34,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application;
+import org.alfresco.web.app.servlet.TemplateContentServlet;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
@@ -46,8 +47,6 @@ import org.alfresco.web.ui.common.component.UIActionLink;
*/
public class SpaceDetailsBean extends BaseDetailsBean
{
- private static final String OUTCOME_RETURN = "showSpaceDetails";
-
private static final String MSG_HAS_FOLLOWING_CATEGORIES = "has_following_categories_space";
private static final String MSG_NO_CATEGORIES_APPLIED = "no_categories_applied_space";
private static final String MSG_ERROR_UPDATE_CATEGORY = "error_update_category";
@@ -60,6 +59,9 @@ public class SpaceDetailsBean extends BaseDetailsBean
private NodeRef addedCategory;
private List categories;
+ /** RSS Template ID */
+ private String rssTemplate;
+
// ------------------------------------------------------------------------------
// Construction
@@ -149,14 +151,6 @@ public class SpaceDetailsBean extends BaseDetailsBean
{
return "space-props";
}
-
- /**
- * @see org.alfresco.web.bean.BaseDetailsBean#getReturnOutcome()
- */
- protected String getReturnOutcome()
- {
- return OUTCOME_RETURN;
- }
// ------------------------------------------------------------------------------
@@ -306,8 +300,8 @@ public class SpaceDetailsBean extends BaseDetailsBean
{
// we know for now that the general classifiable aspect only will be
// applied so we can retrive the categories property direclty
- Collection categories = (Collection)this.nodeService.getProperty(getSpace().getNodeRef(),
- ContentModel.PROP_CATEGORIES);
+ Collection categories = (Collection)this.nodeService.getProperty(
+ getSpace().getNodeRef(), ContentModel.PROP_CATEGORIES);
if (categories == null || categories.size() == 0)
{
@@ -319,16 +313,13 @@ public class SpaceDetailsBean extends BaseDetailsBean
MSG_HAS_FOLLOWING_CATEGORIES));
builder.append("");
- for (Object obj : categories)
+ for (NodeRef ref : categories)
{
- if (obj instanceof NodeRef)
+ if (this.nodeService.exists(ref))
{
- if (this.nodeService.exists((NodeRef)obj))
- {
- builder.append("");
- builder.append(Repository.getNameForNode(this.nodeService, (NodeRef)obj));
- builder.append(" ");
- }
+ builder.append("");
+ builder.append(Repository.getNameForNode(this.nodeService, ref));
+ builder.append(" ");
}
}
builder.append(" ");
@@ -474,4 +465,117 @@ public class SpaceDetailsBean extends BaseDetailsBean
{
return getSpace().isLocked();
}
+
+ /**
+ * @return true if the current space has an RSS feed applied
+ */
+ public boolean isRSSFeed()
+ {
+ return hasRSSFeed(getSpace());
+ }
+
+ /**
+ * @return true if the current space has an RSS feed applied
+ */
+ public static boolean hasRSSFeed(Node space)
+ {
+ return (space.hasAspect(ContentModel.ASPECT_FEEDSOURCE) &&
+ space.getProperties().get(ContentModel.PROP_FEEDTEMPLATE) != null);
+ }
+
+ /**
+ * @return RSS Feed URL for the current space
+ */
+ public String getRSSFeedURL()
+ {
+ return buildRSSFeedURL(getSpace());
+ }
+
+ /**
+ * Build URL for an RSS space based on the 'feedsource' aspect property.
+ *
+ * @param space Node to build RSS template URL for
+ *
+ * @return URL for the RSS feed for a space
+ */
+ public static String buildRSSFeedURL(Node space)
+ {
+ // build RSS feed template URL from selected template and the space NodeRef and
+ // add the guest=true URL parameter - this is required for no login access and
+ // add the mimetype=text/xml URL parameter - required to return correct stream type
+ return TemplateContentServlet.generateURL(space.getNodeRef(),
+ (NodeRef)space.getProperties().get(ContentModel.PROP_FEEDTEMPLATE))
+ + "/rss.xml?guest=true" + "&mimetype=text%2Fxml";
+ }
+
+ /**
+ * @return Returns the current RSS Template ID.
+ */
+ public String getRSSTemplate()
+ {
+ // return current template if it exists
+ NodeRef ref = (NodeRef)getNode().getProperties().get(ContentModel.PROP_FEEDTEMPLATE);
+ return ref != null ? ref.getId() : this.rssTemplate;
+ }
+
+ /**
+ * @param rssTemplate The RSS Template Id to set.
+ */
+ public void setRSSTemplate(String rssTemplate)
+ {
+ this.rssTemplate = rssTemplate;
+ }
+
+ /**
+ * Action handler to apply the selected RSS Template and FeedSource aspect to the current Space
+ */
+ public void applyRSSTemplate(ActionEvent event)
+ {
+ if (this.rssTemplate != null && this.rssTemplate.equals(TemplateSupportBean.NO_SELECTION) == false)
+ {
+ try
+ {
+ // apply the feedsource aspect if required
+ if (getNode().hasAspect(ContentModel.ASPECT_FEEDSOURCE) == false)
+ {
+ this.nodeService.addAspect(getNode().getNodeRef(), ContentModel.ASPECT_FEEDSOURCE, null);
+ }
+
+ // get the selected template Id from the Template Picker
+ NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.rssTemplate);
+
+ // set the template NodeRef into the templatable aspect property
+ this.nodeService.setProperty(getNode().getNodeRef(), ContentModel.PROP_FEEDTEMPLATE, templateRef);
+
+ // reset node details for next refresh of details page
+ getNode().reset();
+ }
+ catch (Exception e)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
+ }
+ }
+ }
+
+ /**
+ * Action handler to remove a RSS template from the current Space
+ */
+ public void removeRSSTemplate(ActionEvent event)
+ {
+ try
+ {
+ // clear template property
+ this.nodeService.setProperty(getNode().getNodeRef(), ContentModel.PROP_FEEDTEMPLATE, null);
+ this.nodeService.removeAspect(getNode().getNodeRef(), ContentModel.ASPECT_FEEDSOURCE);
+
+ // reset node details for next refresh of details page
+ getNode().reset();
+ }
+ catch (Exception e)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
+ }
+ }
}
diff --git a/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java b/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java
new file mode 100644
index 0000000000..6bc9834053
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/TemplateMailHelperBean.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.bean;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.ActionEvent;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.TemplateNode;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.repo.component.template.DefaultModelHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.mail.javamail.MimeMessagePreparator;
+
+/**
+ * @author Kevin Roast
+ */
+public class TemplateMailHelperBean
+{
+ private static Log logger = LogFactory.getLog(TemplateMailHelperBean.class);
+
+ /** JavaMailSender bean reference */
+ protected JavaMailSender mailSender;
+
+ /** NodeService bean reference */
+ protected NodeService nodeService;
+
+ /** dialog state */
+ private String subject = null;
+ private String body = null;
+ private String automaticText = null;
+ private String template = null;
+ private String usingTemplate = null;
+ private String finalBody;
+
+ /**
+ * @param mailSender The JavaMailSender to set.
+ */
+ public void setMailSender(JavaMailSender mailSender)
+ {
+ this.mailSender = mailSender;
+ }
+
+ /**
+ * @param nodeService The nodeService to set.
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * Initialises the bean
+ */
+ public TemplateMailHelperBean()
+ {
+ subject = "";
+ body = "";
+ automaticText = "";
+ template = null;
+ usingTemplate = null;
+ }
+
+ /**
+ * Send an email notification to the specified User authority
+ *
+ * @param person Person node representing the user
+ * @param node Node they are invited too
+ * @param from From text message
+ * @param roleText The role display label for the user invite notification
+ */
+ public void notifyUser(NodeRef person, NodeRef node, final String from, String roleText)
+ {
+ final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL);
+
+ if (to != null && to.length() != 0)
+ {
+ String body = this.body;
+ if (this.usingTemplate != null)
+ {
+ FacesContext fc = FacesContext.getCurrentInstance();
+
+ // use template service to format the email
+ NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate);
+ ServiceRegistry services = Repository.getServiceRegistry(fc);
+ Map model = DefaultModelHelper.buildDefaultModel(
+ services, Application.getCurrentUser(fc), templateRef);
+ model.put("role", roleText);
+ model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null));
+
+ body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model);
+ }
+ this.finalBody = body;
+
+ MimeMessagePreparator mailPreparer = new MimeMessagePreparator()
+ {
+ public void prepare(MimeMessage mimeMessage) throws MessagingException
+ {
+ MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
+ message.setTo(to);
+ message.setSubject(subject);
+ message.setText(finalBody);
+ message.setFrom(from);
+ }
+ };
+
+ if (logger.isDebugEnabled())
+ logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body);
+
+ try
+ {
+ // Send the message
+ this.mailSender.send(mailPreparer);
+ }
+ catch (Throwable e)
+ {
+ // don't stop the action but let admins know email is not getting sent
+ logger.error("Failed to send email to " + to, e);
+ }
+ }
+ }
+
+ /**
+ * Action handler called to insert a template as the email body
+ */
+ public void insertTemplate(ActionEvent event)
+ {
+ if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false)
+ {
+ // get the content of the template so the user can get a basic preview of it
+ try
+ {
+ NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template);
+ ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService();
+ ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT);
+ if (reader != null && reader.exists())
+ {
+ this.body = reader.getContentString();
+
+ this.usingTemplate = this.template;
+ }
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
+ }
+ }
+ }
+
+ /**
+ * Action handler called to discard the template from the email body
+ */
+ public void discardTemplate(ActionEvent event)
+ {
+ this.body = this.automaticText;
+ usingTemplate = null;
+ }
+
+ /**
+ * @return Returns the email body text.
+ */
+ public String getBody()
+ {
+ return this.body;
+ }
+
+ /**
+ * @param body The email body text to set.
+ */
+ public void setBody(String body)
+ {
+ this.body = body;
+ }
+
+ /**
+ * @return Returns the email subject text.
+ */
+ public String getSubject()
+ {
+ return this.subject;
+ }
+
+ /**
+ * @param subject The email subject text to set.
+ */
+ public void setSubject(String subject)
+ {
+ this.subject = subject;
+ }
+
+ /**
+ * @return Returns the automatic text.
+ */
+ public String getAutomaticText()
+ {
+ return this.automaticText;
+ }
+
+ /**
+ * @param automaticText The automatic text to set.
+ */
+ public void setAutomaticText(String automaticText)
+ {
+ this.automaticText = automaticText;
+ }
+
+ /**
+ * @return Returns the email template Id
+ */
+ public String getTemplate()
+ {
+ return this.template;
+ }
+
+ /**
+ * @param template The email template to set.
+ */
+ public void setTemplate(String template)
+ {
+ this.template = template;
+ }
+
+ /**
+ * @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;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/TemplateSupportBean.java b/source/java/org/alfresco/web/bean/TemplateSupportBean.java
index 01f5e59519..29aed02a2a 100644
--- a/source/java/org/alfresco/web/bean/TemplateSupportBean.java
+++ b/source/java/org/alfresco/web/bean/TemplateSupportBean.java
@@ -64,6 +64,9 @@ public class TemplateSupportBean
/** cache of email templates that lasts 30 seconds - enough for a few page refreshes */
private ExpiringValueCache> emailTemplates = new ExpiringValueCache>(1000*30);
+ /** cache of RSS templates that lasts 30 seconds - enough for a few page refreshes */
+ private ExpiringValueCache> rssTemplates = new ExpiringValueCache>(1000*30);
+
/** cache of JavaScript files that lasts 30 seconds - enough for a few page refreshes */
private ExpiringValueCache> scriptFiles = new ExpiringValueCache>(1000*30);
@@ -128,6 +131,28 @@ public class TemplateSupportBean
return templates;
}
+ /**
+ * @return the list of available RSS Templates.
+ */
+ public List getRSSTemplates()
+ {
+ List templates = rssTemplates.get();
+ if (templates == null)
+ {
+ // get the template from the special Email Templates folder
+ FacesContext fc = FacesContext.getCurrentInstance();
+ String xpath = Application.getRootPath(fc) + "/" +
+ Application.getGlossaryFolderName(fc) + "/" +
+ Application.getRSSTemplatesFolderName(fc) + "//*";
+
+ templates = selectDictionaryNodes(fc, xpath, MSG_SELECT_TEMPLATE);
+
+ rssTemplates.put(templates);
+ }
+
+ return templates;
+ }
+
/**
* @return the list of available JavaScript files that can be applied to the current document.
*/
diff --git a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java
index 0549966b2c..219018ca69 100644
--- a/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java
+++ b/source/java/org/alfresco/web/bean/actions/BaseActionWizard.java
@@ -63,7 +63,10 @@ public abstract class BaseActionWizard extends BaseWizardBean
protected List actions;
protected List transformers;
protected List imageTransformers;
- protected List aspects;
+ protected List commonAspects;
+ protected List removableAspects;
+ protected List addableAspects;
+ protected List testableAspects;
protected List users;
protected List encodings;
protected List objectTypes;
@@ -193,7 +196,12 @@ public abstract class BaseActionWizard extends BaseWizardBean
this.actions = new ArrayList();
for (ActionDefinition ruleActionDef : ruleActions)
{
- this.actions.add(new SelectItem(ruleActionDef.getName(), ruleActionDef.getTitle()));
+ String title = ruleActionDef.getTitle();
+ if (title == null || title.length() == 0)
+ {
+ title = ruleActionDef.getName();
+ }
+ this.actions.add(new SelectItem(ruleActionDef.getName(), title));
}
// make sure the list is sorted by the label
@@ -209,70 +217,129 @@ public abstract class BaseActionWizard extends BaseWizardBean
}
/**
- * Returns the aspects that are available
+ * Returns a list of aspects that can be removed
*
- * @return List of SelectItem objects representing the available aspects
+ * @return List of SelectItem objects representing the aspects that can be removed
*/
- public List getAspects()
+ public List getRemovableAspects()
{
- if (this.aspects == null)
+ if (this.removableAspects == null)
{
+ // get the list of common aspects
+ this.removableAspects = new ArrayList();
+ this.removableAspects.addAll(getCommonAspects());
+
+ // get those aspects configured to appear only in the remove aspect action
ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance());
Config wizardCfg = svc.getConfig("Action Wizards");
if (wizardCfg != null)
{
- ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects");
+ ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-remove");
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();
+ List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg);
+ this.removableAspects.addAll(aspects);
}
else
{
- logger.warn("Could not find 'aspects' configuration element");
+ logger.warn("Could not find 'aspects-remove' configuration element");
}
}
else
{
logger.warn("Could not find 'Action Wizards' configuration section");
}
+
+ // make sure the list is sorted by the label
+ QuickSort sorter = new QuickSort(this.removableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
+ sorter.sort();
}
- return this.aspects;
+ return this.removableAspects;
+ }
+
+ /**
+ * Returns a list of aspects that can be added
+ *
+ * @return List of SelectItem objects representing the aspects that can be added
+ */
+ public List getAddableAspects()
+ {
+ if (this.addableAspects == null)
+ {
+ // get the list of common aspects
+ this.addableAspects = new ArrayList();
+ this.addableAspects.addAll(getCommonAspects());
+
+ // get those aspects configured to appear only in the remove aspect action
+ ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance());
+ Config wizardCfg = svc.getConfig("Action Wizards");
+ if (wizardCfg != null)
+ {
+ ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-add");
+ if (aspectsCfg != null)
+ {
+ List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg);
+ this.addableAspects.addAll(aspects);
+ }
+ else
+ {
+ logger.warn("Could not find 'aspects-add' configuration element");
+ }
+ }
+ else
+ {
+ logger.warn("Could not find 'Action Wizards' configuration section");
+ }
+
+ // make sure the list is sorted by the label
+ QuickSort sorter = new QuickSort(this.addableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
+ sorter.sort();
+ }
+
+ return this.addableAspects;
+ }
+
+ /**
+ * Returns a list of aspects that can be tested i.e. hasAspect
+ *
+ * @return List of SelectItem objects representing the aspects that can be tested for
+ */
+ public List getTestableAspects()
+ {
+ if (this.testableAspects == null)
+ {
+ // get the list of common aspects
+ this.testableAspects = new ArrayList();
+ this.testableAspects.addAll(getCommonAspects());
+
+ // get those aspects configured to appear only in the remove aspect action
+ ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance());
+ Config wizardCfg = svc.getConfig("Action Wizards");
+ if (wizardCfg != null)
+ {
+ ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects-test");
+ if (aspectsCfg != null)
+ {
+ List aspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg);
+ this.testableAspects.addAll(aspects);
+ }
+ else
+ {
+ logger.warn("Could not find 'aspects-test' configuration element");
+ }
+ }
+ else
+ {
+ logger.warn("Could not find 'Action Wizards' configuration section");
+ }
+
+ // make sure the list is sorted by the label
+ QuickSort sorter = new QuickSort(this.testableAspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
+ sorter.sort();
+ }
+
+ return this.testableAspects;
}
/**
@@ -846,6 +913,77 @@ public abstract class BaseActionWizard extends BaseWizardBean
}
}
+ /**
+ * Returns the aspects that are available in all scenarios i.e. add, remove and test
+ *
+ * @return List of SelectItem objects representing the available aspects
+ */
+ protected List getCommonAspects()
+ {
+ if (this.commonAspects == null)
+ {
+ ConfigService svc = Application.getConfigService(FacesContext.getCurrentInstance());
+ Config wizardCfg = svc.getConfig("Action Wizards");
+ if (wizardCfg != null)
+ {
+ ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects");
+ if (aspectsCfg != null)
+ {
+ this.commonAspects = readAspectsConfig(FacesContext.getCurrentInstance(), aspectsCfg);
+ }
+ else
+ {
+ logger.warn("Could not find 'aspects' configuration element");
+ }
+ }
+ else
+ {
+ logger.warn("Could not find 'Action Wizards' configuration section");
+ }
+ }
+
+ return this.commonAspects;
+ }
+
+
+ protected List readAspectsConfig(FacesContext context, ConfigElement aspectsCfg)
+ {
+ List 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();
+ }
+ }
+
+ aspects.add(new SelectItem(idQName.toString(), label));
+ }
+ else
+ {
+ logger.warn("Failed to resolve aspect '" + child.getAttribute("name") + "'");
+ }
+ }
+
+ return aspects;
+ }
+
// ------------------------------------------------------------------------------
// Inner classes
diff --git a/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java
index baf02081b6..edd553e04e 100644
--- a/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java
+++ b/source/java/org/alfresco/web/bean/actions/handlers/AddFeaturesHandler.java
@@ -49,7 +49,7 @@ public class AddFeaturesHandler extends BaseActionHandler
String aspect = (String)actionProps.get(PROP_ASPECT);
// find the label used by looking through the SelectItem list
- for (SelectItem item : ((BaseActionWizard)wizard).getAspects())
+ for (SelectItem item : ((BaseActionWizard)wizard).getAddableAspects())
{
if (item.getValue().equals(aspect))
{
diff --git a/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java
index ed0f329c37..9029000ced 100644
--- a/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java
+++ b/source/java/org/alfresco/web/bean/actions/handlers/RemoveFeaturesHandler.java
@@ -49,7 +49,7 @@ public class RemoveFeaturesHandler extends BaseActionHandler
String aspect = (String)actionProps.get(PROP_ASPECT);
// find the label used by looking through the SelectItem list
- for (SelectItem item : ((BaseActionWizard)wizard).getAspects())
+ for (SelectItem item : ((BaseActionWizard)wizard).getRemovableAspects())
{
if (item.getValue().equals(aspect))
{
diff --git a/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java b/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java
index d7b2c1f785..41321fba6e 100644
--- a/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java
+++ b/source/java/org/alfresco/web/bean/actions/handlers/ScriptHandler.java
@@ -35,11 +35,6 @@ public class ScriptHandler extends BaseActionHandler
String id = (String)actionProps.get(PROP_SCRIPT);
NodeRef scriptRef = new NodeRef(Repository.getStoreRef(), id);
repoProps.put(ScriptActionExecutor.PARAM_SCRIPTREF, scriptRef);
-
- NavigationBean navBean = (NavigationBean)FacesHelper.getManagedBean(
- FacesContext.getCurrentInstance(), "NavigationBean");
- repoProps.put(ScriptActionExecutor.PARAM_SPACEREF,
- navBean.getCurrentNode().getNodeRef());
}
public void prepareForEdit(Map actionProps,
diff --git a/source/java/org/alfresco/web/bean/ajax/XFormsBean.java b/source/java/org/alfresco/web/bean/ajax/XFormsBean.java
index 81d3ea3cc4..d596cb69b0 100644
--- a/source/java/org/alfresco/web/bean/ajax/XFormsBean.java
+++ b/source/java/org/alfresco/web/bean/ajax/XFormsBean.java
@@ -196,6 +196,7 @@ public class XFormsBean
* HTTPConnectors. Instance loading and submission then uses these cookies. Important for
* applications using auth.
*/
+ @SuppressWarnings("unchecked")
private static void storeCookies(final javax.servlet.http.Cookie[] cookiesIn,
final ChibaBean chibaBean){
if (cookiesIn != null) {
diff --git a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java
index 7471bd3ba2..89c9b0c81f 100644
--- a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java
+++ b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java
@@ -28,6 +28,9 @@ import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.search.QueryParameterDefImpl;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -38,6 +41,8 @@ import org.alfresco.service.cmr.repository.CopyService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.search.QueryParameterDefinition;
+import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
@@ -81,6 +86,14 @@ public class ClipboardBean
this.copyService = copyService;
}
+ /**
+ * @param searchService The SearchService to set.
+ */
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
/**
* @param navigator The NavigationBean to set.
*/
@@ -244,6 +257,12 @@ public class ClipboardBean
// initial name to attempt the copy of the item with
String name = item.Node.getName();
+ if (action == UIClipboardShelfItem.ACTION_PASTE_LINK)
+ {
+ // copy as link was specifically requested by the user
+ String linkTo = Application.getMessage(FacesContext.getCurrentInstance(), MSG_LINK_TO);
+ name = linkTo + ' ' + name;
+ }
boolean operationComplete = false;
while (operationComplete == false)
@@ -254,56 +273,58 @@ public class ClipboardBean
{
if (action == UIClipboardShelfItem.ACTION_PASTE_LINK)
{
+ // LINK operation
if (logger.isDebugEnabled())
logger.debug("Attempting to link node ID: " + item.Node.getId() + " into node ID: " + destRef.getId());
- // copy as link was specifically requested by the user
-
// we create a special Link Object node that has a property to reference the original
- // use FileFolderService to check if already exists as using nodeService directly here
- String linkTo = Application.getMessage(FacesContext.getCurrentInstance(), MSG_LINK_TO);
-
// create the node using the nodeService (can only use FileFolderService for content)
- Map props = new HashMap(4, 1.0f);
- String linkName = linkTo + ' ' + name;
- props.put(ContentModel.PROP_NAME, linkName + ".lnk");
- props.put(ContentModel.PROP_LINK_DESTINATION, item.Node.getNodeRef());
- if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_CONTENT))
+ if (checkExists(name + ".lnk", destRef) == false)
{
- // create File Link node
- ChildAssociationRef childRef = this.nodeService.createNode(
- destRef,
- ContentModel.ASSOC_CONTAINS,
- assocRef.getQName(),
- ContentModel.TYPE_FILELINK,
- props);
+ Map props = new HashMap(2, 1.0f);
+ props.put(ContentModel.PROP_NAME, name + ".lnk");
+ props.put(ContentModel.PROP_LINK_DESTINATION, item.Node.getNodeRef());
+ if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_CONTENT))
+ {
+ // create File Link node
+ ChildAssociationRef childRef = this.nodeService.createNode(
+ destRef,
+ ContentModel.ASSOC_CONTAINS,
+ assocRef.getQName(),
+ ContentModel.TYPE_FILELINK,
+ props);
+
+ // apply the titled aspect - title and description
+ Map titledProps = new HashMap(2, 1.0f);
+ titledProps.put(ContentModel.PROP_TITLE, name);
+ titledProps.put(ContentModel.PROP_DESCRIPTION, name);
+ this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps);
+ }
+ else
+ {
+ // create Folder link node
+ ChildAssociationRef childRef = this.nodeService.createNode(
+ destRef,
+ ContentModel.ASSOC_CONTAINS,
+ assocRef.getQName(),
+ ContentModel.TYPE_FOLDERLINK,
+ props);
+
+ // apply the uifacets aspect - icon, title and description props
+ Map uiFacetsProps = new HashMap(4, 1.0f);
+ uiFacetsProps.put(ContentModel.PROP_ICON, "space-icon-link");
+ uiFacetsProps.put(ContentModel.PROP_TITLE, name);
+ uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, name);
+ this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_UIFACETS, uiFacetsProps);
+ }
- // apply the titled aspect - title and description
- Map titledProps = new HashMap(2, 1.0f);
- titledProps.put(ContentModel.PROP_TITLE, linkName);
- titledProps.put(ContentModel.PROP_DESCRIPTION, linkName);
- this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps);
- }
- else
- {
- // create Folder link node
- ChildAssociationRef childRef = this.nodeService.createNode(
- destRef,
- ContentModel.ASSOC_CONTAINS,
- assocRef.getQName(),
- ContentModel.TYPE_FOLDERLINK,
- props);
-
- // apply the uifacets aspect - icon, title and description props
- Map uiFacetsProps = new HashMap(3, 1.0f);
- uiFacetsProps.put(ContentModel.PROP_ICON, "space-icon-link");
- uiFacetsProps.put(ContentModel.PROP_TITLE, linkName);
- uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, linkName);
- this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_UIFACETS, uiFacetsProps);
+ // if we get here without an exception, the clipboard link operation was successful
+ operationComplete = true;
}
}
else
{
+ // COPY operation
if (logger.isDebugEnabled())
logger.debug("Attempting to copy node ID: " + item.Node.getId() + " into node ID: " + destRef.getId());
@@ -326,17 +347,24 @@ public class ClipboardBean
else
{
// copy the node
- this.copyService.copy(
- item.Node.getNodeRef(),
- destRef,
- ContentModel.ASSOC_CONTAINS,
- assocRef.getQName(),
- true);
+ if (checkExists(name, destRef) == false)
+ {
+ this.copyService.copy(
+ item.Node.getNodeRef(),
+ destRef,
+ ContentModel.ASSOC_CONTAINS,
+ assocRef.getQName(),
+ true);
+ }
}
+
+ // if we get here without an exception, the clipboard copy operation was successful
+ operationComplete = true;
}
}
else
{
+ // MOVE operation
if (logger.isDebugEnabled())
logger.debug("Attempting to move node ID: " + item.Node.getId() + " into node ID: " + destRef.getId());
@@ -358,27 +386,57 @@ public class ClipboardBean
ContentModel.ASSOC_CONTAINS,
assocRef.getQName());
}
+
+ // if we get here without an exception, the clipboard move operation was successful
+ operationComplete = true;
}
-
- // if we get here without an exception, the clipboard operation was successful
- operationComplete = true;
}
catch (FileExistsException fileExistsErr)
{
- if (item.Mode == ClipboardStatus.COPY)
+ if (item.Mode != ClipboardStatus.COPY)
{
- String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF);
- name = copyOf + ' ' + name;
- }
- else
- {
- // we should not rename an item when it is being moved
+ // we should not rename an item when it is being moved - so exit
throw fileExistsErr;
}
}
+ if (operationComplete == false)
+ {
+ String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF);
+ name = copyOf + ' ' + name;
+ }
}
}
+ private boolean checkExists(String name, NodeRef parent)
+ {
+ ServiceRegistry services = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
+
+ QueryParameterDefinition[] params = new QueryParameterDefinition[1];
+ params[0] = new QueryParameterDefImpl(
+ ContentModel.PROP_NAME,
+ services.getDictionaryService().getDataType(
+ DataTypeDefinition.TEXT),
+ true,
+ name);
+
+ // execute the query
+ List nodeRefs = searchService.selectNodes(
+ parent,
+ XPATH_QUERY_NODE_MATCH,
+ params,
+ services.getNamespaceService(),
+ false);
+
+ return (nodeRefs.size() != 0);
+ }
+
+ /** Shallow search for nodes with a name pattern */
+ private static final String XPATH_QUERY_NODE_MATCH =
+ "./*" +
+ "[like(@cm:name, $cm:name, false)]";// +
+ //" and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
+ //" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
+
/**
* Add a clipboard node for an operation to the clipboard
*
@@ -437,6 +495,9 @@ public class ClipboardBean
/** The CopyService to be used by the bean */
protected CopyService copyService;
+ /** The SearchService to be used by the bean */
+ protected SearchService searchService;
+
/** The NavigationBean reference */
protected NavigationBean navigator;
diff --git a/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java b/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java
index 04cc367d9b..edaa8f7dea 100644
--- a/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java
+++ b/source/java/org/alfresco/web/bean/content/EditContentPropertiesDialog.java
@@ -198,7 +198,7 @@ public class EditContentPropertiesDialog extends BaseDialogBean
{
return MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_EXISTS),
- ((FileExistsException)exception).getExisting().getName());
+ ((FileExistsException)exception).getName());
}
else if (exception instanceof InvalidNodeRefException)
{
diff --git a/source/java/org/alfresco/web/bean/dashboard/PageConfig.java b/source/java/org/alfresco/web/bean/dashboard/PageConfig.java
index f59b271a76..65d8dbed76 100644
--- a/source/java/org/alfresco/web/bean/dashboard/PageConfig.java
+++ b/source/java/org/alfresco/web/bean/dashboard/PageConfig.java
@@ -42,7 +42,7 @@ import org.dom4j.io.XMLWriter;
*
* @author Kevin Roast
*/
-final class PageConfig
+public final class PageConfig
{
private static Log logger = LogFactory.getLog(DashboardManager.class);
@@ -55,7 +55,7 @@ final class PageConfig
private static final String ATTR_REFID = "idref";
private List pages = new ArrayList(4);
- private int currentPageIndex = 0;
+ private Page currentPage = null;
/**
@@ -86,13 +86,30 @@ final class PageConfig
*/
public Page getCurrentPage()
{
- if (currentPageIndex < pages.size())
+ if (this.currentPage == null)
{
- return pages.get(currentPageIndex);
+ if (this.pages.size() != 0)
+ {
+ this.currentPage = pages.get(0);
+ }
}
- else
+ return this.currentPage;
+ }
+
+ /**
+ * Set the current Page for the cnfig
+ *
+ * @param pageId ID of the page to set as current
+ */
+ public void setCurrentPage(String pageId)
+ {
+ for (Page page : pages)
{
- return null;
+ if (page.getId().equals(pageId))
+ {
+ this.currentPage = page;
+ break;
+ }
}
}
diff --git a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java
index b0c4918589..586eb6bd83 100644
--- a/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java
+++ b/source/java/org/alfresco/web/bean/dialog/BaseDialogBean.java
@@ -2,6 +2,7 @@ package org.alfresco.web.bean.dialog;
import java.text.MessageFormat;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
@@ -19,6 +20,7 @@ import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
import org.alfresco.web.ui.common.Utils;
/**
@@ -96,13 +98,18 @@ public abstract class BaseDialogBean implements IDialogBean
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
- Utils.addErrorMessage(formatErrorMessage(e));
+ Utils.addErrorMessage(formatErrorMessage(e), e);
outcome = getErrorOutcome(e);
}
}
return outcome;
}
+
+ public List getAdditionalButtons()
+ {
+ return null;
+ }
public String getCancelButtonLabel()
{
diff --git a/source/java/org/alfresco/web/bean/dialog/DialogManager.java b/source/java/org/alfresco/web/bean/dialog/DialogManager.java
index a588eb5c77..c47c6d8c62 100644
--- a/source/java/org/alfresco/web/bean/dialog/DialogManager.java
+++ b/source/java/org/alfresco/web/bean/dialog/DialogManager.java
@@ -1,5 +1,7 @@
package org.alfresco.web.bean.dialog;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
@@ -9,6 +11,7 @@ import javax.faces.event.ActionEvent;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
import org.alfresco.web.config.DialogsConfigElement.DialogConfig;
import org.alfresco.web.ui.common.component.UIActionLink;
@@ -147,6 +150,17 @@ public class DialogManager
return desc;
}
+ /**
+ * Returns the id of a configured action group representing the actions to
+ * display for the dialog.
+ *
+ * @return The action group id
+ */
+ public String getActions()
+ {
+ return this.currentDialogConfig.getActionsConfigId();
+ }
+
/**
* Returns the page the dialog will use
*
@@ -157,6 +171,51 @@ public class DialogManager
return this.currentDialogConfig.getPage();
}
+ /**
+ * Determines whether the current dialog's OK button is visible
+ *
+ * @return true if the OK button is visible, false if it's not
+ */
+ public boolean isOKButtonVisible()
+ {
+ return this.currentDialogConfig.isOKButtonVisible();
+ }
+
+ /**
+ * Returns a list of additional buttons to display in the dialog
+ *
+ * @return List of button configurations
+ */
+ public List getAdditionalButtons()
+ {
+ List buttons = null;
+
+ // get a list of buttons to display from the configuration
+ List cfgButtons = this.currentDialogConfig.getButtons();
+
+ // get a list of buttons added dynamically by the dialog
+ List dynButtons = this.currentDialog.getAdditionalButtons();
+
+ if (cfgButtons != null && dynButtons != null)
+ {
+ // combine the two lists
+ buttons = new ArrayList(
+ cfgButtons.size() + dynButtons.size());
+ buttons.addAll(cfgButtons);
+ buttons.addAll(dynButtons);
+ }
+ else if (cfgButtons != null && dynButtons == null)
+ {
+ buttons = cfgButtons;
+ }
+ else if (cfgButtons == null && dynButtons != null)
+ {
+ buttons = dynButtons;
+ }
+
+ return buttons;
+ }
+
/**
* Returns the label to use for the cancel button
*
diff --git a/source/java/org/alfresco/web/bean/dialog/IDialogBean.java b/source/java/org/alfresco/web/bean/dialog/IDialogBean.java
index 354129db01..fe2e9b59dc 100644
--- a/source/java/org/alfresco/web/bean/dialog/IDialogBean.java
+++ b/source/java/org/alfresco/web/bean/dialog/IDialogBean.java
@@ -1,7 +1,10 @@
package org.alfresco.web.bean.dialog;
+import java.util.List;
import java.util.Map;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
+
/**
* Interface that defines the contract for a dialog backing bean
*
@@ -30,6 +33,13 @@ public interface IDialogBean
*/
public String finish();
+ /**
+ * Returns a list of additional buttons to display in the dialog.
+ *
+ * @return List of button configurations, null if there are no buttons
+ */
+ public List getAdditionalButtons();
+
/**
* Returns the label to use for the cancel button
*
diff --git a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java
index 8137ec929b..3766266bea 100644
--- a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java
+++ b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java
@@ -32,6 +32,7 @@ import org.alfresco.web.ui.repo.component.property.BaseAssociationEditor;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIProperty;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
+import org.alfresco.web.ui.repo.component.property.UISeparator;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet.ClientValidation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -83,6 +84,12 @@ public abstract class BaseComponentGenerator implements IComponentGenerator
// setup any converter the property needs
setupConverter(context, propertySheet, item, propertyDef, component);
}
+ else if (item instanceof UISeparator)
+ {
+ // just create the component and add it
+ component = createComponent(context, propertySheet, item);
+ item.getChildren().add(component);
+ }
else
{
// get the association definition
diff --git a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java
index f0068f32e5..a7f0463ddb 100644
--- a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java
+++ b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java
@@ -1,7 +1,10 @@
package org.alfresco.web.bean.generator;
+import java.util.Date;
+
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
+import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
@@ -11,6 +14,7 @@ import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.common.converter.XMLDateConverter;
import org.alfresco.web.ui.repo.RepoConstants;
+import org.alfresco.web.ui.repo.component.UIMultiValueEditor;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
@@ -21,8 +25,43 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
*/
public class DatePickerGenerator extends BaseComponentGenerator
{
+ private int yearCount = 30;
+ private int startYear = new Date().getYear() + 1900 + 2;
+
private static final String MSG_DATE = "date_pattern";
+ /**
+ * @return Returns the year to start counting back from
+ */
+ public int getStartYear()
+ {
+ return startYear;
+ }
+
+ /**
+ * @param startYear Sets the year to start counting back from
+ */
+ public void setStartYear(int startYear)
+ {
+ this.startYear = startYear;
+ }
+
+ /**
+ * @return Returns the number of years to show
+ */
+ public int getYearCount()
+ {
+ return yearCount;
+ }
+
+ /**
+ * @param yearCount Sets the number of years to show
+ */
+ public void setYearCount(int yearCount)
+ {
+ this.yearCount = yearCount;
+ }
+
@SuppressWarnings("unchecked")
public UIComponent generate(FacesContext context, String id)
{
@@ -30,7 +69,8 @@ public class DatePickerGenerator extends BaseComponentGenerator
createComponent(ComponentConstants.JAVAX_FACES_INPUT);
component.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
FacesHelper.setupComponentId(context, component, id);
- component.getAttributes().put("yearCount", new Integer(30));
+ component.getAttributes().put("startYear", this.startYear);
+ component.getAttributes().put("yearCount", this.yearCount);
component.getAttributes().put("style", "margin-right: 7px;");
return component;
@@ -59,8 +99,26 @@ public class DatePickerGenerator extends BaseComponentGenerator
UIPropertySheet propertySheet, PropertySheetItem item,
UIComponent component, boolean realTimeChecking, String idSuffix)
{
- // a date picker will always have a date value so there
- // is no need to create a mandatory validation rule
+ if (component instanceof UIMultiValueEditor)
+ {
+ // Override the setup of the mandatory validation
+ // so we can send the _current_value id suffix.
+ // We also enable real time so the page load
+ // check disables the ok button if necessary, as the user
+ // adds or removes items from the multi value list the
+ // page will be refreshed and therefore re-check the status.
+
+ super.setupMandatoryValidation(context, propertySheet, item,
+ component, true, "_current_value");
+ }
+ else
+ {
+ // setup the client validation rule with real time validation enabled
+ // so that the initial page load checks the state of the date
+ super.setupMandatoryValidation(context, propertySheet, item,
+ component, true, idSuffix);
+ }
+
}
/**
diff --git a/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java b/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java
new file mode 100644
index 0000000000..2cb6e31f9b
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/generator/HeaderSeparatorGenerator.java
@@ -0,0 +1,23 @@
+package org.alfresco.web.bean.generator;
+
+import javax.faces.component.UIComponent;
+
+import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
+
+/**
+ * Generates a component to represent a separator that gets rendered
+ * as a header.
+ *
+ * @author gavinc
+ */
+public class HeaderSeparatorGenerator extends SeparatorGenerator
+{
+ @Override
+ protected String getHtml(UIComponent component, PropertySheetItem item)
+ {
+ String html = " " +
+ item.getDisplayLabel() + "
";
+
+ return html;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java b/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java
new file mode 100644
index 0000000000..49076c1fa1
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/generator/SeparatorGenerator.java
@@ -0,0 +1,49 @@
+package org.alfresco.web.bean.generator;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
+import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
+
+/**
+ * Generates a component to represent a separator.
+ *
+ * @author gavinc
+ */
+public class SeparatorGenerator extends BaseComponentGenerator
+{
+ @SuppressWarnings("unchecked")
+ public UIComponent generate(FacesContext context, String id)
+ {
+ UIComponent component = this.createOutputTextComponent(context, id);
+ component.getAttributes().put("escape", Boolean.FALSE);
+
+ return component;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected UIComponent createComponent(FacesContext context, UIPropertySheet propertySheet,
+ PropertySheetItem item)
+ {
+ UIComponent component = this.generate(context, item.getName());
+
+ // set the HTML to use
+ component.getAttributes().put("value", getHtml(component, item));
+
+ return component;
+ }
+
+ /**
+ * Returns the HTML to display for the separator
+ *
+ * @param component The JSF component representing the separator
+ * @param item The separator item
+ * @return The HTML
+ */
+ protected String getHtml(UIComponent component, PropertySheetItem item)
+ {
+ return "
";
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java b/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java
new file mode 100644
index 0000000000..7a2af9bc09
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/generator/TextAreaGenerator.java
@@ -0,0 +1,64 @@
+package org.alfresco.web.bean.generator;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.alfresco.web.app.servlet.FacesHelper;
+import org.alfresco.web.ui.common.ComponentConstants;
+
+/**
+ * Generates a text field component.
+ *
+ * @author gavinc
+ */
+public class TextAreaGenerator extends TextFieldGenerator
+{
+ private int rows = 3;
+ private int columns = 32;
+
+ /**
+ * @return Returns the number of columns
+ */
+ public int getColumns()
+ {
+ return columns;
+ }
+
+ /**
+ * @param columns Sets the number of columns
+ */
+ public void setColumns(int columns)
+ {
+ this.columns = columns;
+ }
+
+ /**
+ * @return Returns the number of rows
+ */
+ public int getRows()
+ {
+ return rows;
+ }
+
+ /**
+ * @param rows Sets the number of rows
+ */
+ public void setRows(int rows)
+ {
+ this.rows = rows;
+ }
+
+ @SuppressWarnings("unchecked")
+ public UIComponent generate(FacesContext context, String id)
+ {
+ UIComponent component = context.getApplication().
+ createComponent(ComponentConstants.JAVAX_FACES_INPUT);
+ component.setRendererType(ComponentConstants.JAVAX_FACES_TEXTAREA);
+ FacesHelper.setupComponentId(context, component, id);
+
+ component.getAttributes().put("rows", this.rows);
+ component.getAttributes().put("cols", this.columns);
+
+ return component;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java
index cef4447498..cdbe96d52e 100644
--- a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java
+++ b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java
@@ -26,6 +26,41 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
*/
public class TextFieldGenerator extends BaseComponentGenerator
{
+ private int size = 35;
+ private int maxLength = 1024;
+
+ /**
+ * @return Returns the default size for a text field
+ */
+ public int getSize()
+ {
+ return size;
+ }
+
+ /**
+ * @param size Sets the size of a text field
+ */
+ public void setSize(int size)
+ {
+ this.size = size;
+ }
+
+ /**
+ * @return Returns the max length for the text field
+ */
+ public int getMaxLength()
+ {
+ return maxLength;
+ }
+
+ /**
+ * @param maxLength Sets the max length of the text field
+ */
+ public void setMaxLength(int maxLength)
+ {
+ this.maxLength = maxLength;
+ }
+
@SuppressWarnings("unchecked")
public UIComponent generate(FacesContext context, String id)
{
@@ -33,8 +68,9 @@ public class TextFieldGenerator extends BaseComponentGenerator
createComponent(ComponentConstants.JAVAX_FACES_INPUT);
component.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
FacesHelper.setupComponentId(context, component, id);
- component.getAttributes().put("size", "35");
- component.getAttributes().put("maxlength", "1024");
+
+ component.getAttributes().put("size", this.size);
+ component.getAttributes().put("maxlength", this.maxLength);
return component;
}
@@ -48,12 +84,17 @@ public class TextFieldGenerator extends BaseComponentGenerator
if (propertySheet.inEditMode())
{
- // if the field has the list of values constraint a
- // SelectOne component is required otherwise create
- // the standard edit component
+ // if the field has the list of values constraint
+ // and it is editable a SelectOne component is
+ // required otherwise create the standard edit component
ListOfValuesConstraint constraint = getListOfValuesConstraint(
context, propertySheet, item);
- if (constraint != null)
+
+ PropertyDefinition propDef = this.getPropertyDefinition(context,
+ propertySheet.getNode(), item.getName());
+
+ if (constraint != null && item.isReadOnly() == false &&
+ propDef != null && propDef.isProtected() == false)
{
component = context.getApplication().createComponent(
UISelectOne.COMPONENT_TYPE);
diff --git a/source/java/org/alfresco/web/bean/repository/Node.java b/source/java/org/alfresco/web/bean/repository/Node.java
index f0a6e49e12..cfab7794b1 100644
--- a/source/java/org/alfresco/web/bean/repository/Node.java
+++ b/source/java/org/alfresco/web/bean/repository/Node.java
@@ -36,8 +36,6 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.web.app.Application;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* Lighweight client side representation of a node held in the repository.
@@ -48,28 +46,25 @@ public class Node implements Serializable
{
private static final long serialVersionUID = 3544390322739034169L;
- protected static final Log logger = LogFactory.getLog(Node.class);
-
protected NodeRef nodeRef;
- private String name;
- private QName type;
- private String path;
- private String id;
- private Set aspects = null;
- private Map permissions;
- private Boolean locked = null;
- private Boolean workingCopyOwner = null;
+ protected String name;
+ protected QName type;
+ protected String path;
+ protected String id;
+ protected Set aspects = null;
+ protected Map permissions;
+ protected Boolean locked = null;
+ protected Boolean workingCopyOwner = null;
protected QNameNodeMap properties;
protected boolean propsRetrieved = false;
protected ServiceRegistry services = null;
+ protected boolean childAssocsRetrieved = false;
+ protected QNameNodeMap childAssociations;
+ protected boolean assocsRetrieved = false;
+ protected QNameNodeMap associations;
- private boolean childAssocsRetrieved = false;
- private QNameNodeMap childAssociations;
private Map> childAssociationsAdded;
private Map> childAssociationsRemoved;
-
- private boolean assocsRetrieved = false;
- private QNameNodeMap associations;
private Map> associationsAdded;
private Map> associationsRemoved;
@@ -94,7 +89,7 @@ public class Node implements Serializable
/**
* @return All the properties known about this node.
*/
- public Map getProperties()
+ public final Map getProperties()
{
if (this.propsRetrieved == false)
{
@@ -120,7 +115,7 @@ public class Node implements Serializable
{
if (this.assocsRetrieved == false)
{
- associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
+ this.associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
List assocs = getServiceRegistry().getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
@@ -341,7 +336,7 @@ public class Node implements Serializable
*
* @return true if the permission is applied to the node for this user, false otherwise
*/
- public final boolean hasPermission(String permission)
+ public boolean hasPermission(String permission)
{
Boolean valid = null;
if (permissions != null)
diff --git a/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java b/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java
index 92c0ecdc18..d12f40f28f 100644
--- a/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java
+++ b/source/java/org/alfresco/web/bean/repository/QNameNodeMap.java
@@ -16,15 +16,11 @@
*/
package org.alfresco.web.bean.repository;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import java.util.Set;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QNameMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
/**
* A extension of the repo QNameMap to provide custom property resolving support for Node wrappers.
@@ -86,6 +82,7 @@ public final class QNameNodeMap extends QNameMap implements Map, Cloneable
/**
* @see java.util.Map#get(java.lang.Object)
*/
+ @SuppressWarnings("unchecked")
public Object get(Object key)
{
String qnameKey = Repository.resolveToQNameString(key.toString());
@@ -120,6 +117,7 @@ public final class QNameNodeMap extends QNameMap implements Map, Cloneable
/**
* Shallow copy the map by copying keys and values into a new QNameNodeMap
*/
+ @SuppressWarnings("unchecked")
public Object clone()
{
QNameNodeMap map = new QNameNodeMap(this.resolver, this.parent);
diff --git a/source/java/org/alfresco/web/bean/repository/TransientMapNode.java b/source/java/org/alfresco/web/bean/repository/TransientMapNode.java
new file mode 100644
index 0000000000..301d987ac8
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/repository/TransientMapNode.java
@@ -0,0 +1,149 @@
+package org.alfresco.web.bean.repository;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Represents a transient node i.e. it is not and will not be present in the repository.
+ *
+ * This type of node is typically used to drive rich lists where the Map implementation
+ * is required for sorting columns.
+ *
+ *
+ * @author gavinc
+ */
+public class TransientMapNode extends TransientNode implements Map
+{
+ private static final long serialVersionUID = 1120307465342597322L;
+
+ /**
+ * Constructor.
+ *
+ * NOTE: The name is NOT automatically added to the map of properties,
+ * if you need the name of this node to be in the map then add it to
+ * the map passed in to this constructor.
+ *
+ *
+ * @param type The type this node will represent
+ * @param name The name of the node
+ * @param data The properties and associations this node will have
+ */
+ public TransientMapNode(QName type, String name, Map data)
+ {
+ super(type, name, data);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Transient map node of type: " + getType() +
+ "\nProperties: " + this.getProperties().toString();
+ }
+
+ // ------------------------------------------------------------------------------
+ // Map implementation - allows the Node bean to be accessed using JSF expression syntax
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear()
+ {
+ getProperties().clear();
+ }
+
+ /**
+ * @see java.util.Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(Object key)
+ {
+ return getProperties().containsKey(key);
+ }
+
+ /**
+ * @see java.util.Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(Object value)
+ {
+ return getProperties().containsKey(value);
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ @SuppressWarnings("unchecked")
+ public Set entrySet()
+ {
+ return getProperties().entrySet();
+ }
+
+ /**
+ * @see java.util.Map#get(java.lang.Object)
+ */
+ public Object get(Object key)
+ {
+ return getProperties().get(key);
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty()
+ {
+ return getProperties().isEmpty();
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ @SuppressWarnings("unchecked")
+ public Set keySet()
+ {
+ return getProperties().keySet();
+ }
+
+ /**
+ * @see java.util.Map#put(K, V)
+ */
+ public Object put(String key, Object value)
+ {
+ return getProperties().put(key, value);
+ }
+
+ /**
+ * @see java.util.Map#putAll(java.util.Map)
+ */
+ @SuppressWarnings("unchecked")
+ public void putAll(Map t)
+ {
+ getProperties().putAll(t);
+ }
+
+ /**
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ public Object remove(Object key)
+ {
+ return getProperties().remove(key);
+ }
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size()
+ {
+ return getProperties().size();
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ @SuppressWarnings("unchecked")
+ public Collection values()
+ {
+ return getProperties().values();
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/repository/TransientNode.java b/source/java/org/alfresco/web/bean/repository/TransientNode.java
new file mode 100644
index 0000000000..50a2c11e32
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/repository/TransientNode.java
@@ -0,0 +1,150 @@
+package org.alfresco.web.bean.repository;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.service.cmr.dictionary.AspectDefinition;
+import org.alfresco.service.cmr.dictionary.AssociationDefinition;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a transient node i.e. it is not and will not be present in the repository.
+ *
+ * This type of node is typically used to drive the property sheet when data collection
+ * is required for a type but the node does not need to be stored in the repository. An
+ * example use is the workflow, transient nodes are used to collect workitem metadata.
+ *
+ *
+ * @author gavinc
+ */
+public class TransientNode extends Node
+{
+ private static final long serialVersionUID = 2140554155948154106L;
+
+ private static final Log logger = LogFactory.getLog(TransientNode.class);
+
+ /**
+ * Constructor.
+ *
+ * NOTE: The name is NOT automatically added to the map of properties,
+ * if you need the name of this node to be in the map then add it to
+ * the map passed in to this constructor.
+ *
+ *
+ * @param type The type this node will represent
+ * @param name The name of the node
+ * @param data The properties and associations this node will have
+ */
+ public TransientNode(QName type, String name, Map data)
+ {
+ // create a dummy NodeRef to pass to the constructor
+ super(new NodeRef(Repository.getStoreRef(), GUID.generate()));
+
+ this.type = type;
+ this.name = name;
+
+ // initialise the node
+ initNode(data);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Constructed transient node: " + this);
+ }
+
+ /**
+ * Initialises the node.
+ *
+ * @param data The properties and associations to initialise the node with
+ */
+ protected void initNode(Map data)
+ {
+ // setup the transient node so that the super class methods work
+ // and do not need to go back to the repository
+
+ DictionaryService ddService = this.getServiceRegistry().getDictionaryService();
+
+ // marshall the given properties and associations into the internal maps
+ this.associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
+ this.childAssociations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
+
+ if (data != null)
+ {
+ // go through all data items and allocate to the correct internal list
+ for (QName item : data.keySet())
+ {
+ PropertyDefinition propDef = ddService.getProperty(item);
+ if (propDef != null)
+ {
+ this.properties.put(item, data.get(item));
+ }
+ else
+ {
+ // see if the item is either type of association
+ AssociationDefinition assocDef = ddService.getAssociation(item);
+ if (assocDef != null)
+ {
+ if (assocDef.isChild())
+ {
+ this.childAssociations.put(item, data.get(item));
+ }
+ else
+ {
+ this.associations.put(item, data.get(item));
+ }
+ }
+ }
+ }
+ }
+
+ // show that the maps have been initialised
+ this.propsRetrieved = true;
+ this.assocsRetrieved = true;
+ this.childAssocsRetrieved = true;
+
+ // setup the list of aspects the node would have
+ TypeDefinition typeDef = ddService.getType(this.type);
+ if (typeDef == null)
+ {
+ throw new AlfrescoRuntimeException("Failed to find type definition for start task: " + this.type);
+ }
+
+ this.aspects = new HashSet();
+ for (AspectDefinition aspectDef : typeDef.getDefaultAspects())
+ {
+ this.aspects.add(aspectDef.getName());
+ }
+
+ // setup remaining variables
+ this.path = "";
+ this.locked = Boolean.FALSE;
+ this.workingCopyOwner = Boolean.FALSE;
+ }
+
+ @Override
+ public boolean hasPermission(String permission)
+ {
+ return true;
+ }
+
+ @Override
+ public void reset()
+ {
+ // don't reset anything otherwise we'll lose our data
+ // with no way of getting it back!!
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Transient node of type: " + getType() +
+ "\nProperties: " + this.getProperties().toString();
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/repository/User.java b/source/java/org/alfresco/web/bean/repository/User.java
index cfcb58a2bb..99bbd61bc3 100644
--- a/source/java/org/alfresco/web/bean/repository/User.java
+++ b/source/java/org/alfresco/web/bean/repository/User.java
@@ -64,6 +64,16 @@ public final class User
this.person = person;
}
+ /**
+ * Forces a clear of any cached or calcluated values
+ */
+ public void reset()
+ {
+ this.fullName = null;
+ this.administrator = null;
+ this.preferences = null;
+ }
+
/**
* @return The user name
*/
@@ -114,7 +124,6 @@ public final class User
{
return this.ticket;
}
-
/**
* @return Returns the person NodeRef
diff --git a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java
index eb59e93ff3..1b8be56344 100644
--- a/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java
+++ b/source/java/org/alfresco/web/bean/rules/CreateRuleWizard.java
@@ -19,6 +19,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionConditionDefinition;
+import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
@@ -67,6 +68,7 @@ public class CreateRuleWizard extends BaseActionWizard
protected boolean runInBackground;
protected boolean applyToSubSpaces;
protected boolean editingCondition;
+ protected boolean ruleDisabled;
private static final Log logger = LogFactory.getLog(CreateRuleWizard.class);
@@ -84,6 +86,7 @@ public class CreateRuleWizard extends BaseActionWizard
this.condition = null;
this.applyToSubSpaces = false;
this.runInBackground = false;
+ this.ruleDisabled = false;
this.conditions = null;
this.allConditionsProperties = new ArrayList>();
@@ -100,7 +103,9 @@ public class CreateRuleWizard extends BaseActionWizard
Node currentSpace = this.browseBean.getActionSpace();
// create the new rule
- Rule rule = this.ruleService.createRule(this.getType());
+ //Rule rule = this.ruleService.createRule(this.getType());
+ Rule rule = new Rule();
+ rule.setRuleType(this.getType());
// setup the rule
outcome = setupRule(context, rule, outcome);
@@ -189,12 +194,13 @@ public class CreateRuleWizard extends BaseActionWizard
String backgroundYesNo = this.runInBackground ? bundle.getString("yes") : bundle.getString("no");
String subSpacesYesNo = this.applyToSubSpaces ? bundle.getString("yes") : bundle.getString("no");
+ String ruleDisabledYesNo = this.ruleDisabled ? bundle.getString("yes") : bundle.getString("no");
return buildSummary(
new String[] {bundle.getString("rule_type"), bundle.getString("name"), bundle.getString("description"),
- bundle.getString("apply_to_sub_spaces"), bundle.getString("run_in_background"),
+ bundle.getString("apply_to_sub_spaces"), bundle.getString("run_in_background"), bundle.getString("rule_disabled"),
bundle.getString("conditions"), bundle.getString("actions")},
- new String[] {this.type, this.title, this.description, subSpacesYesNo, backgroundYesNo,
+ new String[] {this.type, this.title, this.description, subSpacesYesNo, backgroundYesNo, ruleDisabledYesNo,
conditionsSummary.toString(), actionsSummary.toString()});
}
@@ -203,6 +209,21 @@ public class CreateRuleWizard extends BaseActionWizard
{
return "error_rule";
}
+
+ protected CompositeAction getCompositeAction(Rule rule)
+ {
+ // Get the composite action
+ Action ruleAction = rule.getAction();
+ if (ruleAction == null)
+ {
+ throw new AlfrescoRuntimeException("Rule does not have associated action.");
+ }
+ else if ((ruleAction instanceof CompositeAction) == false)
+ {
+ throw new AlfrescoRuntimeException("Rules with non-composite actions are not currently supported by the UI");
+ }
+ return (CompositeAction)ruleAction;
+ }
// ------------------------------------------------------------------------------
// Bean Getters and Setters
@@ -437,6 +458,22 @@ public class CreateRuleWizard extends BaseActionWizard
this.applyToSubSpaces = applyToSubSpaces;
}
+ /**
+ * @return Returns whether the rule is disabled or not.
+ */
+ public boolean getRuleDisabled()
+ {
+ return this.ruleDisabled;
+ }
+
+ /**
+ * @param ruleDisabled Sets whether the rule is disabled or not
+ */
+ public void setRuleDisabled(boolean ruleDisabled)
+ {
+ this.ruleDisabled = ruleDisabled;
+ }
+
/**
* @return Returns the type.
*/
@@ -650,6 +687,10 @@ public class CreateRuleWizard extends BaseActionWizard
rule.setDescription(this.description);
rule.applyToChildren(this.applyToSubSpaces);
rule.setExecuteAsynchronously(this.runInBackground);
+ rule.setRuleDisabled(this.ruleDisabled);
+
+ CompositeAction compositeAction = this.actionService.createCompositeAction();
+ rule.setAction(compositeAction);
// add all the conditions to the rule
for (Map condParams : this.allConditionsProperties)
@@ -674,7 +715,7 @@ public class CreateRuleWizard extends BaseActionWizard
Boolean not = (Boolean)condParams.get(BaseConditionHandler.PROP_CONDITION_NOT);
condition.setInvertCondition(((Boolean)not).booleanValue());
- rule.addActionCondition(condition);
+ compositeAction.addActionCondition(condition);
}
// add all the actions to the rule
@@ -696,7 +737,7 @@ public class CreateRuleWizard extends BaseActionWizard
// add the action to the rule
Action action = this.actionService.createAction(actionName);
action.setParameterValues(repoActionParams);
- rule.addAction(action);
+ compositeAction.addAction(action);
}
return outcome;
diff --git a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java
index 437d44063b..bd4e13059e 100644
--- a/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java
+++ b/source/java/org/alfresco/web/bean/rules/EditRuleWizard.java
@@ -12,6 +12,7 @@ import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionConditionDefinition;
import org.alfresco.service.cmr.action.ActionDefinition;
+import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.web.bean.actions.IHandler;
import org.alfresco.web.bean.repository.Node;
@@ -46,16 +47,20 @@ public class EditRuleWizard extends CreateRuleWizard
}
// populate the bean with current values
- this.type = rule.getRuleTypeName();
+ this.type = rule.getRuleTypes().get(0);
this.title = rule.getTitle();
this.description = rule.getDescription();
this.applyToSubSpaces = rule.isAppliedToChildren();
- this.runInBackground = rule.getExecuteAsychronously();
+ this.runInBackground = rule.getExecuteAsynchronously();
+ this.ruleDisabled = rule.getRuleDisabled();
FacesContext context = FacesContext.getCurrentInstance();
+ // Get the composite action
+ CompositeAction compositeAction = getCompositeAction(rule);
+
// populate the conditions list with maps of properties representing each condition
- List conditions = rule.getActionConditions();
+ List conditions = compositeAction.getActionConditions();
for (ActionCondition condition : conditions)
{
this.currentConditionProperties = new HashMap(3);
@@ -90,7 +95,7 @@ public class EditRuleWizard extends CreateRuleWizard
}
// populate the actions list with maps of properties representing each action
- List actions = rule.getActions();
+ List actions = compositeAction.getActions();
for (Action action : actions)
{
this.currentActionProperties = new HashMap(3);
@@ -135,10 +140,13 @@ public class EditRuleWizard extends CreateRuleWizard
// get the existing rule
Rule rule = this.rulesBean.getCurrentRule();
+
+ // Get the composite action
+ CompositeAction compositeAction = getCompositeAction(rule);
// remove all the conditions and actions from the current rule
- rule.removeAllActionConditions();
- rule.removeAllActions();
+ compositeAction.removeAllActionConditions();
+ compositeAction.removeAllActions();
// re-setup the rule
outcome = setupRule(context, rule, outcome);
diff --git a/source/java/org/alfresco/web/bean/rules/RulesBean.java b/source/java/org/alfresco/web/bean/rules/RulesBean.java
index e3dc802b17..2a45f58c61 100644
--- a/source/java/org/alfresco/web/bean/rules/RulesBean.java
+++ b/source/java/org/alfresco/web/bean/rules/RulesBean.java
@@ -27,10 +27,13 @@ import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ExecuteAllRulesActionExecuter;
+import org.alfresco.repo.rule.RuleModel;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.web.app.Application;
@@ -55,6 +58,8 @@ public class RulesBean implements IContextListener
{
private static final String MSG_ERROR_DELETE_RULE = "error_delete_rule";
private static final String MSG_REAPPLY_RULES_SUCCESS = "reapply_rules_success";
+ private static final String MSG_IGNORE_INHERTIED_RULES = "ignore_inherited_rules";
+ private static final String MSG_INCLUDE_INHERITED_RULES = "include_inherited_rules";
private static final String LOCAL = "local";
private static final String INHERITED = "inherited";
@@ -67,6 +72,7 @@ public class RulesBean implements IContextListener
private Rule currentRule;
private UIRichList richList;
private ActionService actionService;
+ private NodeService nodeService;
/**
@@ -116,7 +122,11 @@ public class RulesBean implements IContextListener
// wrap them all passing the current space
for (Rule rule : repoRules)
{
- WrappedRule wrapped = new WrappedRule(rule, getSpace().getNodeRef());
+ Date createdDate = (Date)this.nodeService.getProperty(rule.getNodeRef(), ContentModel.PROP_CREATED);
+ Date modifiedDate = (Date)this.nodeService.getProperty(rule.getNodeRef(), ContentModel.PROP_MODIFIED);
+ boolean isLocal = getSpace().getNodeRef().equals(this.ruleService.getOwningNodeRef(rule));
+
+ WrappedRule wrapped = new WrappedRule(rule, isLocal, createdDate, modifiedDate);
this.rules.add(wrapped);
}
@@ -138,13 +148,13 @@ public class RulesBean implements IContextListener
if (logger.isDebugEnabled())
logger.debug("Rule clicked, it's id is: " + id);
- this.currentRule = this.ruleService.getRule(
- getSpace().getNodeRef(), id);
+ this.currentRule = this.ruleService.getRule(new NodeRef(id));
+ //getSpace().getNodeRef(), id);
// refresh list
contextUpdated();
}
- }
+ }
/**
* Reapply the currently defines rules to the
@@ -193,6 +203,52 @@ public class RulesBean implements IContextListener
}
}
+ /**
+ * Gets the label id from the ignore inhertied action
+ *
+ * @return the message id
+ */
+ public String getIgnoreInheritedRulesLabelId()
+ {
+ FacesContext fc = FacesContext.getCurrentInstance();
+ String result = Application.getMessage(fc, MSG_IGNORE_INHERTIED_RULES);
+
+ if (this.nodeService.hasAspect(this.getSpace().getNodeRef(), RuleModel.ASPECT_IGNORE_INHERITED_RULES) == true)
+ {
+ result = Application.getMessage(fc, MSG_INCLUDE_INHERITED_RULES);
+ }
+ return result;
+ }
+
+ public boolean getIgnoreInheritedRules()
+ {
+ return this.nodeService.hasAspect(this.getSpace().getNodeRef(), RuleModel.ASPECT_IGNORE_INHERITED_RULES);
+ }
+
+ /**
+ * Action listener to ignore (or include) inherited rules.
+ *
+ * @param event the action event object
+ */
+ public void ignoreInheritedRules(ActionEvent event)
+ {
+ NodeRef nodeRef = this.getSpace().getNodeRef();
+ if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == true)
+ {
+ this.nodeService.removeAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES);
+ }
+ else
+ {
+ this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null);
+ }
+
+ // force the list to be re-queried when the page is refreshed
+ if (this.richList != null)
+ {
+ this.richList.setValue(null);
+ }
+ }
+
/**
* Returns the current rule
*
@@ -306,6 +362,16 @@ public class RulesBean implements IContextListener
{
this.actionService = actionService;
}
+
+ /**
+ * Set the node service to use
+ *
+ * @param nodeService the node service
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
// ------------------------------------------------------------------------------
@@ -330,7 +396,9 @@ public class RulesBean implements IContextListener
public static class WrappedRule
{
private Rule rule;
- private NodeRef ruleNode;
+ private boolean isLocal;
+ private Date createdDate;
+ private Date modifiedDate;
/**
* Constructs a RuleWrapper object
@@ -338,10 +406,12 @@ public class RulesBean implements IContextListener
* @param rule The rule we are wrapping
* @param ruleNode The node the rules belong to
*/
- public WrappedRule(Rule rule, NodeRef ruleNode)
+ public WrappedRule(Rule rule, boolean isLocal, Date createdDate, Date modifiedDate)
{
this.rule = rule;
- this.ruleNode = ruleNode;
+ this.isLocal = isLocal;
+ this.createdDate = createdDate;
+ this.modifiedDate = modifiedDate;
}
/**
@@ -362,7 +432,7 @@ public class RulesBean implements IContextListener
*/
public boolean getLocal()
{
- return ruleNode.equals(this.rule.getOwningNodeRef());
+ return this.isLocal;
}
/** Methods to support sorting of the rules list in a table */
@@ -374,7 +444,7 @@ public class RulesBean implements IContextListener
*/
public String getId()
{
- return this.rule.getId();
+ return this.rule.getNodeRef().toString();
}
/**
@@ -404,7 +474,7 @@ public class RulesBean implements IContextListener
*/
public Date getCreatedDate()
{
- return this.rule.getCreatedDate();
+ return this.createdDate;
}
/**
@@ -414,7 +484,7 @@ public class RulesBean implements IContextListener
*/
public Date getModifiedDate()
{
- return this.rule.getModifiedDate();
+ return this.modifiedDate;
}
}
}
diff --git a/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java b/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java
index 5510ec6316..e04025845c 100644
--- a/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java
+++ b/source/java/org/alfresco/web/bean/rules/handlers/HasAspectHandler.java
@@ -49,7 +49,7 @@ public class HasAspectHandler extends BaseConditionHandler
String label = null;
String aspectName = (String)conditionProps.get(PROP_ASPECT);
- for (SelectItem item : ((CreateRuleWizard)wizard).getAspects())
+ for (SelectItem item : ((CreateRuleWizard)wizard).getTestableAspects())
{
if (item.getValue().equals(aspectName))
{
diff --git a/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java b/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java
index 6fffd00e31..70d9ecae90 100644
--- a/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java
+++ b/source/java/org/alfresco/web/bean/spaces/CreateSpaceWizard.java
@@ -712,7 +712,7 @@ public class CreateSpaceWizard extends BaseWizardBean
{
return MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_EXISTS),
- ((FileExistsException)exception).getExisting().getName());
+ ((FileExistsException)exception).getName());
}
else
{
diff --git a/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java b/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java
index 5001db6ca6..2409f1ae78 100644
--- a/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java
+++ b/source/java/org/alfresco/web/bean/spaces/DeleteSpaceDialog.java
@@ -1,15 +1,27 @@
package org.alfresco.web.bean.spaces;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
+import javax.transaction.UserTransaction;
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.content.DeleteContentDialog;
import org.alfresco.web.bean.dialog.BaseDialogBean;
+import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
+import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -22,6 +34,14 @@ import org.apache.commons.logging.LogFactory;
public class DeleteSpaceDialog extends BaseDialogBean
{
private static final Log logger = LogFactory.getLog(DeleteContentDialog.class);
+
+ private static final String DELETE_ALL = "all";
+ private static final String DELETE_FILES = "files";
+ private static final String DELETE_FOLDERS = "folders";
+ private static final String DELETE_CONTENTS = "contents";
+
+ private String deleteMode = DELETE_ALL;
+
// ------------------------------------------------------------------------------
// Dialog implementation
@@ -34,10 +54,84 @@ public class DeleteSpaceDialog extends BaseDialogBean
Node node = this.browseBean.getActionSpace();
if (node != null)
{
+ // force cache of name property so we can use it after the delete
+ node.getName();
+
if (logger.isDebugEnabled())
- logger.debug("Trying to delete space: " + node.getId());
+ logger.debug("Trying to delete space: " + node.getId() + " using delete mode: " + this.deleteMode);
+
+ if (DELETE_ALL.equals(this.deleteMode))
+ {
+ this.nodeService.deleteNode(node.getNodeRef());
+ }
+ else
+ {
+ List childRefs = this.nodeService.getChildAssocs(node.getNodeRef(),
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+ List deleteRefs = new ArrayList(childRefs.size());
+ for (ChildAssociationRef ref : childRefs)
+ {
+ NodeRef nodeRef = ref.getChildRef();
+
+ if (this.nodeService.exists(nodeRef))
+ {
+ if (DELETE_CONTENTS.equals(this.deleteMode))
+ {
+ deleteRefs.add(nodeRef);
+ }
+ else
+ {
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.nodeService.getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.dictionaryService.getType(type);
+
+ if (typeDef != null)
+ {
+ if (DELETE_FOLDERS.equals(this.deleteMode))
+ {
+ // look for folder type
+ if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true &&
+ this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false)
+ {
+ deleteRefs.add(nodeRef);
+ }
+ }
+ else if (DELETE_FILES.equals(this.deleteMode))
+ {
+ // look for content file type
+ if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT))
+ {
+ deleteRefs.add(nodeRef);
+ }
+ }
+ }
+ }
+ }
+ }
- this.nodeService.deleteNode(node.getNodeRef());
+ // delete the list of refs
+ TransactionService txService = Repository.getServiceRegistry(context).getTransactionService();
+ for (NodeRef nodeRef : deleteRefs)
+ {
+ UserTransaction tx = null;
+
+ try
+ {
+ tx = txService.getNonPropagatingUserTransaction();
+ tx.begin();
+
+ this.nodeService.deleteNode(nodeRef);
+
+ tx.commit();
+ }
+ catch (Throwable err)
+ {
+ try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
+ }
+ }
+ }
}
else
{
@@ -52,21 +146,28 @@ public class DeleteSpaceDialog extends BaseDialogBean
{
Node node = this.browseBean.getActionSpace();
- // remove this node from the breadcrumb if required
- this.browseBean.removeSpaceFromBreadcrumb(node);
-
- // add a message to inform the user that the delete was OK
- String statusMsg = MessageFormat.format(
- Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"),
- new Object[]{node.getName()});
- Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg);
-
- // clear action context
- this.browseBean.setActionSpace(null);
-
- // setting the outcome will show the browse view again
- return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
- AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
+ if (node != null && this.nodeService.exists(node.getNodeRef()) == false)
+ {
+ // remove this node from the breadcrumb if required
+ this.browseBean.removeSpaceFromBreadcrumb(node);
+
+ // add a message to inform the user that the delete was OK
+ String statusMsg = MessageFormat.format(
+ Application.getMessage(FacesContext.getCurrentInstance(), "status_space_deleted"),
+ new Object[]{node.getName()});
+ Utils.addStatusMessage(FacesMessage.SEVERITY_INFO, statusMsg);
+
+ // clear action context
+ this.browseBean.setActionSpace(null);
+
+ // setting the outcome will show the browse view again
+ return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
+ AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
+ }
+ else
+ {
+ return outcome;
+ }
}
@Override
@@ -81,6 +182,7 @@ public class DeleteSpaceDialog extends BaseDialogBean
return false;
}
+
// ------------------------------------------------------------------------------
// Bean Getters and Setters
@@ -97,4 +199,20 @@ public class DeleteSpaceDialog extends BaseDialogBean
return MessageFormat.format(fileConfirmMsg,
new Object[] {this.browseBean.getActionSpace().getName()});
}
+
+ /**
+ * @return Returns the delete operation mode.
+ */
+ public String getDeleteMode()
+ {
+ return this.deleteMode;
+ }
+
+ /**
+ * @param deleteMode The delete operation mode to set.
+ */
+ public void setDeleteMode(String deleteMode)
+ {
+ this.deleteMode = deleteMode;
+ }
}
diff --git a/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java
new file mode 100644
index 0000000000..53eb16465f
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/users/EmailSpaceUsersDialog.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.bean.users;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.ActionEvent;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.AccessPermission;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.cmr.security.AuthorityService;
+import org.alfresco.service.cmr.security.AuthorityType;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.app.context.IContextListener;
+import org.alfresco.web.app.context.UIContextService;
+import org.alfresco.web.bean.TemplateMailHelperBean;
+import org.alfresco.web.bean.dialog.BaseDialogBean;
+import org.alfresco.web.bean.repository.MapNode;
+import org.alfresco.web.bean.repository.Node;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.bean.repository.User;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.repo.WebResources;
+import org.alfresco.web.ui.repo.component.UIUserGroupPicker;
+import org.alfresco.web.ui.repo.component.UIUserGroupPicker.PickerEvent;
+import org.springframework.mail.javamail.JavaMailSender;
+
+/**
+ * Dialog bean managing the state for the Email Space Users page. Calculates the user/groups
+ * that are invited to a space and builds the data structures needed to display and modify
+ * the list in the web-client UI. Notifies the selected user/groups with a templatable email.
+ *
+ * @author Kevin Roast
+ */
+public class EmailSpaceUsersDialog extends BaseDialogBean implements IContextListener
+{
+ private static final String PROP_DUPLICATE = "duplicate";
+ private static final String PROP_PARENT = "parent";
+ private static final String PROP_ID = "id";
+ private static final String PROP_ISGROUP = "isGroup";
+ private static final String PROP_ICON = "icon";
+ private static final String PROP_FULLNAME = "fullName";
+ private static final String PROP_ROLES = "roles";
+ private static final String PROP_EXPANDED = "expanded";
+ private static final String PROP_SELECTED = "selected";
+ private static final String PROP_USERNAME = "userName";
+
+ /** Injected Bean references */
+ protected PermissionService permissionService;
+ protected PersonService personService;
+ protected AuthorityService authorityService;
+ protected JavaMailSender mailSender;
+
+ /** Helper providing template based mailing facilities */
+ protected TemplateMailHelperBean mailHelper;
+
+ /** List of user/group property map/node instances */
+ private List usersGroups = null;
+
+ /** Quick lookup table of authority to user/group instance */
+ private Map userGroupLookup = new HashMap();
+
+
+ /**
+ * Default constructor
+ */
+ public EmailSpaceUsersDialog()
+ {
+ UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
+ }
+
+ /**
+ * Setup the dialog
+ */
+ public void init(Map parameters)
+ {
+ super.init(parameters);
+
+ mailHelper = new TemplateMailHelperBean();
+ mailHelper.setMailSender(mailSender);
+ mailHelper.setNodeService(nodeService);
+ }
+
+ /**
+ * @see org.alfresco.web.bean.dialog.BaseDialogBean#finishImpl(javax.faces.context.FacesContext, java.lang.String)
+ */
+ @Override
+ protected String finishImpl(FacesContext context, String outcome) throws Exception
+ {
+ // get the space ref this mail applies to
+ NodeRef spaceRef = getSpace().getNodeRef();
+
+ // calculate the 'from' email address
+ User user = Application.getCurrentUser(context);
+ String from = (String)this.nodeService.getProperty(user.getPerson(), ContentModel.PROP_EMAIL);
+ if (from == null || from.length() == 0)
+ {
+ // if the user does not have an email address get the default one from the config service
+ from = Application.getClientConfig(context).getFromEmailAddress();
+ }
+
+ Set mailedAuthorities = new HashSet(usersGroups.size());
+
+ // walk the list of users/groups to notify
+ for (Map node : usersGroups)
+ {
+ String authority = (String)node.get(PROP_USERNAME);
+ boolean selected = (Boolean)node.get(PROP_SELECTED);
+
+ // if User, email then, else if Group get all members and email them
+ AuthorityType authType = AuthorityType.getAuthorityType(authority);
+ if (authType.equals(AuthorityType.USER))
+ {
+ if (selected == true && this.personService.personExists(authority))
+ {
+ if (mailedAuthorities.contains(authority) == false)
+ {
+ this.mailHelper.notifyUser(
+ this.personService.getPerson(authority), spaceRef, from, (String)node.get(PROP_ROLES));
+ mailedAuthorities.add(authority);
+ }
+ }
+ }
+ else if (authType.equals(AuthorityType.GROUP))
+ {
+ // is the group expanded? if so we'll deal with the child authorities instead
+ boolean expanded = (Boolean)node.get(PROP_EXPANDED);
+ if (expanded == false && selected == true)
+ {
+ // notify all members of the group
+ Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, authority, false);
+ for (String userAuth : users)
+ {
+ if (this.personService.personExists(userAuth) == true)
+ {
+ if (mailedAuthorities.contains(userAuth) == false)
+ {
+ this.mailHelper.notifyUser(
+ this.personService.getPerson(userAuth), spaceRef, from, (String)node.get(PROP_ROLES));
+ mailedAuthorities.add(userAuth);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return outcome;
+ }
+
+
+ // ------------------------------------------------------------------------------
+ // IContextListener implementation
+
+ /**
+ * @see org.alfresco.web.app.context.IContextListener#contextUpdated()
+ */
+ public void contextUpdated()
+ {
+ this.usersGroups = null;
+ this.userGroupLookup = new HashMap();
+ }
+
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * @param permissionService The PermissionService to set
+ */
+ public void setPermissionService(PermissionService permissionService)
+ {
+ this.permissionService = permissionService;
+ }
+
+ /**
+ * @param permissionService The PersonService to set
+ */
+ public void setPersonService(PersonService personService)
+ {
+ this.personService = personService;
+ }
+
+ /**
+ * @param mailSender The JavaMailSender to set.
+ */
+ public void setMailSender(JavaMailSender mailSender)
+ {
+ this.mailSender = mailSender;
+ }
+
+ /**
+ * @param authorityService The AuthorityService to set.
+ */
+ public void setAuthorityService(AuthorityService authorityService)
+ {
+ this.authorityService = authorityService;
+ }
+
+ /**
+ * @return The space to email users for
+ */
+ public Node getSpace()
+ {
+ return this.browseBean.getActionSpace();
+ }
+
+ /**
+ * Return the List of objects representing the Users and Groups invited to this space.
+ * The picker is then responsible for rendering a view to represent those users and groups
+ * which allows the users to select and deselect users and groups, also to expand groups
+ * to show sub-groups and users.
+ *
+ * @return List of Map objects representing the users/groups assigned to the current space
+ */
+ public List getUsersGroups()
+ {
+ if (this.usersGroups == null)
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ UserTransaction tx = null;
+ try
+ {
+ tx = Repository.getUserTransaction(context, true);
+ tx.begin();
+
+ // Return all the permissions set against the current node
+ // for any authentication instance (user/group).
+ // Then combine them into a single list for each authentication found.
+ Map> permissionMap = new HashMap>(8, 1.0f);
+ Set permissions = permissionService.getAllSetPermissions(getSpace().getNodeRef());
+ for (AccessPermission permission : permissions)
+ {
+ // we are only interested in Allow and not groups/owner etc.
+ if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
+ (permission.getAuthorityType() == AuthorityType.USER ||
+ permission.getAuthorityType() == AuthorityType.GROUP ||
+ permission.getAuthorityType() == AuthorityType.GUEST ||
+ permission.getAuthorityType() == AuthorityType.EVERYONE))
+ {
+ String authority = permission.getAuthority();
+
+ List userPermissions = permissionMap.get(authority);
+ if (userPermissions == null)
+ {
+ // create for first time
+ userPermissions = new ArrayList(4);
+ permissionMap.put(authority, userPermissions);
+ }
+ // add the permission name for this authority
+ userPermissions.add(permission.getPermission());
+ }
+ }
+
+ // create the structure as a linked list for fast insert/removal of items
+ this.usersGroups = new LinkedList();
+
+ // for each authentication (username/group key) found we get the Person
+ // node represented by it and use that for our list databinding object
+ for (String authority : permissionMap.keySet())
+ {
+ Map node = buildAuthorityMap(authority, UserMembersBean.roleListToString(context, permissionMap.get(authority)));
+ if (node != null)
+ {
+ this.usersGroups.add(node);
+ }
+ }
+
+ // commit the transaction
+ tx.commit();
+ }
+ catch (InvalidNodeRefException refErr)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) );
+ this.usersGroups = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ context, Repository.ERROR_GENERIC), err.getMessage()), err );
+ this.usersGroups = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ }
+ return this.usersGroups;
+ }
+
+ /**
+ * Build a Map representing a user/group with a set of useful property values required
+ * by the UIUserGroupPicker UI component.
+ *
+ * @param authority User/Group authority
+ * @param roles Role text for the authority
+ *
+ * @return Map
+ */
+ private Map buildAuthorityMap(String authority, String roles)
+ {
+ Map node = null;
+
+ if (AuthorityType.getAuthorityType(authority) == AuthorityType.GUEST ||
+ this.personService.personExists(authority))
+ {
+ NodeRef nodeRef = this.personService.getPerson(authority);
+ if (nodeRef != null)
+ {
+ // create our Node representation
+ node = new MapNode(nodeRef);
+
+ // set data binding properties
+ // this will also force initialisation of the props now during the UserTransaction
+ // it is much better for performance to do this now rather than during page bind
+ Map props = ((MapNode)node).getProperties();
+ props.put(PROP_FULLNAME, ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
+ props.put(PROP_ICON, WebResources.IMAGE_PERSON);
+ props.put(PROP_ISGROUP, false);
+ }
+ }
+ else if (AuthorityType.getAuthorityType(authority) == AuthorityType.GROUP)
+ {
+ // need a map (dummy node) to represent props for this Group Authority
+ node = new HashMap(8, 1.0f);
+ if (authority.startsWith(PermissionService.GROUP_PREFIX) == true)
+ {
+ node.put(PROP_FULLNAME, authority.substring(PermissionService.GROUP_PREFIX.length()));
+ }
+ else
+ {
+ node.put(PROP_FULLNAME, authority);
+ }
+ node.put(PROP_USERNAME, authority);
+ node.put(PROP_ID, authority);
+ node.put(PROP_ICON, WebResources.IMAGE_GROUP);
+ node.put(PROP_ISGROUP, true);
+ node.put(PROP_EXPANDED, false);
+ }
+ if (node != null)
+ {
+ // add the common properties
+ node.put(PROP_ROLES, roles);
+ node.put(PROP_PARENT, null);
+
+ if (this.userGroupLookup.get(authority) != null)
+ {
+ // this authority already exists in the list somewhere else - mark as duplicate
+ node.put(PROP_DUPLICATE, true);
+ node.put(PROP_SELECTED, false);
+ }
+ else
+ {
+ // add to table for the first time, not a duplicate
+ this.userGroupLookup.put(authority, node);
+ node.put(PROP_DUPLICATE, false);
+ node.put(PROP_SELECTED, true);
+ }
+ }
+
+ return node;
+ }
+
+ /**
+ * @return TemplateMailHelperBean instance for this wizard
+ */
+ public TemplateMailHelperBean getMailHelper()
+ {
+ return this.mailHelper;
+ }
+
+
+ // ------------------------------------------------------------------------------
+ // Action Event Listeners
+
+ /**
+ * Action handler for a user/group selector event
+ */
+ public void userGroupSelectorAction(ActionEvent event)
+ {
+ if (event instanceof PickerEvent)
+ {
+ PickerEvent pickerEvent = (PickerEvent)event;
+
+ // find the user/group this event represents
+ Map userGroup = null;
+ int index = 0;
+ for (; index authorities = authorityService.getContainedAuthorities(
+ null, pickerEvent.Authority, true);
+ for (String authority : authorities)
+ {
+ Map node = buildAuthorityMap(authority, (String)userGroup.get(PROP_ROLES));
+ if (node != null)
+ {
+ node.put(PROP_PARENT, userGroup);
+ node.put(PROP_SELECTED, selected);
+ this.usersGroups.add(++index, node);
+ }
+ }
+ }
+ else
+ {
+ // remove the children for the group
+ for (index++; index> permissionMap = new HashMap>(13, 1.0f);
+ Map> permissionMap = new HashMap>(8, 1.0f);
Set permissions = permissionService.getAllSetPermissions(getNode().getNodeRef());
- if (permissions != null)
+ for (AccessPermission permission : permissions)
{
- for (AccessPermission permission : permissions)
+ // we are only interested in Allow and not groups/owner etc.
+ if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
+ (permission.getAuthorityType() == AuthorityType.USER ||
+ permission.getAuthorityType() == AuthorityType.GROUP ||
+ permission.getAuthorityType() == AuthorityType.GUEST ||
+ permission.getAuthorityType() == AuthorityType.EVERYONE))
{
- // we are only interested in Allow and not groups/owner etc.
- if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
- (permission.getAuthorityType() == AuthorityType.USER ||
- permission.getAuthorityType() == AuthorityType.GROUP ||
- permission.getAuthorityType() == AuthorityType.GUEST ||
- permission.getAuthorityType() == AuthorityType.EVERYONE))
+ String authority = permission.getAuthority();
+
+ List userPermissions = permissionMap.get(authority);
+ if (userPermissions == null)
{
- String authority = permission.getAuthority();
-
- List userPermissions = permissionMap.get(authority);
- if (userPermissions == null)
- {
- // create for first time
- userPermissions = new ArrayList(4);
- permissionMap.put(authority, userPermissions);
- }
- // add the permission name for this authority
- userPermissions.add(permission.getPermission());
+ // create for first time
+ userPermissions = new ArrayList(4);
+ permissionMap.put(authority, userPermissions);
}
+ // add the permission name for this authority
+ userPermissions.add(permission.getPermission());
}
}
- // for each authentication (username key) found we get the Person
+ // for each authentication (username/group key) found we get the Person
// node represented by it and use that for our list databinding object
personNodes = new ArrayList(permissionMap.size());
for (String authority : permissionMap.keySet())
@@ -337,7 +334,7 @@ public abstract class UserMembersBean implements IContextListener
// it is much better for performance to do this now rather than during page bind
Map props = node.getProperties();
props.put("fullName", ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
- props.put("roles", listToString(context, permissionMap.get(authority)));
+ props.put("roles", roleListToString(context, permissionMap.get(authority)));
props.put("icon", WebResources.IMAGE_PERSON);
personNodes.add(node);
@@ -357,7 +354,7 @@ public abstract class UserMembersBean implements IContextListener
}
node.put("userName", authority);
node.put("id", authority);
- node.put("roles", listToString(context, permissionMap.get(authority)));
+ node.put("roles", roleListToString(context, permissionMap.get(authority)));
node.put("icon", WebResources.IMAGE_GROUP);
personNodes.add(node);
}
@@ -369,7 +366,7 @@ public abstract class UserMembersBean implements IContextListener
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
- context, Repository.ERROR_NODEREF), new Object[] {"root"}) );
+ context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) );
personNodes = Collections.emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
@@ -384,7 +381,16 @@ public abstract class UserMembersBean implements IContextListener
return personNodes;
}
- private static String listToString(FacesContext context, List list)
+ /**
+ * Convert a list of user Roles to a comma separated string list. Each individual role
+ * will be looked up in message bundle to convert to a human readable string value.
+ *
+ * @param context FacesContext
+ * @param list List of Role names
+ *
+ * @return Comma separated string of human readable roles
+ */
+ public static String roleListToString(FacesContext context, List list)
{
StringBuilder buf = new StringBuilder();
diff --git a/source/java/org/alfresco/web/bean/users/UsersBean.java b/source/java/org/alfresco/web/bean/users/UsersBean.java
index a03197a32d..5653d090f0 100644
--- a/source/java/org/alfresco/web/bean/users/UsersBean.java
+++ b/source/java/org/alfresco/web/bean/users/UsersBean.java
@@ -16,6 +16,7 @@
*/
package org.alfresco.web.bean.users;
+import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -35,6 +36,8 @@ import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.ISO9075;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
@@ -264,10 +267,10 @@ public class UsersBean implements IContextListener
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
-
+
// remember the Person node
setPerson(node);
-
+
// clear the UI state in preparation for finishing the action
// and returning to the main page
contextUpdated();
@@ -387,7 +390,39 @@ public class UsersBean implements IContextListener
return outcome;
}
-
+
+ /**
+ * Action handler called for the OK button press
+ */
+ public String changeUserDetails()
+ {
+ String outcome = DIALOG_CLOSE;
+
+ FacesContext context = FacesContext.getCurrentInstance();
+ try
+ {
+ Map props = this.nodeService.getProperties(getPerson().getNodeRef());
+ props.put(ContentModel.PROP_FIRSTNAME,
+ (String)getPerson().getProperties().get(ContentModel.PROP_FIRSTNAME));
+ props.put(ContentModel.PROP_LASTNAME,
+ (String)getPerson().getProperties().get(ContentModel.PROP_LASTNAME));
+ props.put(ContentModel.PROP_EMAIL,
+ (String)getPerson().getProperties().get(ContentModel.PROP_EMAIL));
+
+ // persist changes
+ this.nodeService.setProperties(getPerson().getNodeRef(), props);
+
+ // if the above call was successful, then reset Person Node in the session
+ Application.getCurrentUser(context).reset();
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ context, Repository.ERROR_GENERIC), err.getMessage()), err );
+ }
+
+ return outcome;
+ }
/**
* Event handler called when the user wishes to search for a user
@@ -413,10 +448,11 @@ public class UsersBean implements IContextListener
tx.begin();
// define the query to find people by their first or last name
+ String search = ISO9075.encode(this.searchCriteria);
String query = "( TYPE:\"{http://www.alfresco.org/model/content/1.0}person\") AND " +
- "((@\\{http\\://www.alfresco.org/model/content/1.0\\}firstName:" + this.searchCriteria +
- "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}lastName:" + this.searchCriteria +
- "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}userName:" + this.searchCriteria +
+ "((@\\{http\\://www.alfresco.org/model/content/1.0\\}firstName:" + search +
+ "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}lastName:" + search +
+ "*) OR (@\\{http\\://www.alfresco.org/model/content/1.0\\}userName:" + search +
"*)))";
if (logger.isDebugEnabled())
diff --git a/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java b/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java
index df0afdfb48..2a150fc77a 100644
--- a/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java
+++ b/source/java/org/alfresco/web/bean/wizard/InviteUsersWizard.java
@@ -20,7 +20,6 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
@@ -30,16 +29,10 @@ import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.repository.ContentReader;
-import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
@@ -47,20 +40,16 @@ import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
-import org.alfresco.web.bean.TemplateSupportBean;
+import org.alfresco.web.bean.TemplateMailHelperBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.SortableSelectItem;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIGenericPicker;
-import org.alfresco.web.ui.repo.component.template.DefaultModelHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.mail.javamail.MimeMessagePreparator;
/**
* @author Kevin Roast
@@ -96,6 +85,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
/** personService bean reference */
protected PersonService personService;
+ /** Helper providing template based mailing facilities */
+ protected TemplateMailHelperBean mailHelper;
+
/** datamodel for table of roles for users */
private DataModel userRolesDataModel = null;
@@ -104,12 +96,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
/** dialog state */
private String notify = NOTIFY_YES;
- private String subject = null;
- private String body = null;
- private String automaticText = null;
- private String template = null;
- private String usingTemplate = null;
- private String finalBody;
/**
* @return a cached list of available permissions for the type being dealt with
@@ -175,11 +161,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
notify = NOTIFY_YES;
userGroupRoles = new ArrayList(8);
- subject = "";
- body = "";
- automaticText = "";
- template = null;
- usingTemplate = null;
+ mailHelper = new TemplateMailHelperBean();
+ mailHelper.setMailSender(mailSender);
+ mailHelper.setNodeService(nodeService);
}
/**
@@ -239,7 +223,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
{
if (this.personService.personExists(authority) == true)
{
- notifyUser(this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole());
+ this.mailHelper.notifyUser(
+ this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole());
}
}
else if (authType.equals(AuthorityType.GROUP))
@@ -250,7 +235,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
{
if (this.personService.personExists(userAuth) == true)
{
- notifyUser(this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole());
+ this.mailHelper.notifyUser(
+ this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole());
}
}
}
@@ -274,65 +260,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
return outcome;
}
- /**
- * Send an email notification to the specified User authority
- *
- * @param person Person node representing the user
- * @param node Node they are invited too
- * @param from From text message
- * @param roleText The role display label for the user invite notification
- */
- private void notifyUser(NodeRef person, NodeRef node, final String from, String roleText)
- {
- final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL);
-
- if (to != null && to.length() != 0)
- {
- String body = this.body;
- if (this.usingTemplate != null)
- {
- FacesContext fc = FacesContext.getCurrentInstance();
-
- // use template service to format the email
- NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate);
- ServiceRegistry services = Repository.getServiceRegistry(fc);
- Map model = DefaultModelHelper.buildDefaultModel(
- services, Application.getCurrentUser(fc), templateRef);
- model.put("role", roleText);
- model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null));
-
- body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model);
- }
- this.finalBody = body;
-
- MimeMessagePreparator mailPreparer = new MimeMessagePreparator()
- {
- public void prepare(MimeMessage mimeMessage) throws MessagingException
- {
- MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
- message.setTo(to);
- message.setSubject(subject);
- message.setText(finalBody);
- message.setFrom(from);
- }
- };
-
- if (logger.isDebugEnabled())
- logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body);
-
- try
- {
- // Send the message
- this.mailSender.send(mailPreparer);
- }
- catch (Throwable e)
- {
- // don't stop the action but let admins know email is not getting sent
- logger.error("Failed to send email to " + to, e);
- }
- }
- }
-
/**
* Returns the properties for current user-roles JSF DataModel
*
@@ -552,43 +479,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
return roles;
}
- /**
- * Action handler called to insert a template as the email body
- */
- public void insertTemplate(ActionEvent event)
- {
- if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false)
- {
- // get the content of the template so the user can get a basic preview of it
- try
- {
- NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template);
- ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService();
- ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT);
- if (reader != null && reader.exists())
- {
- this.body = reader.getContentString();
-
- this.usingTemplate = this.template;
- }
- }
- catch (Throwable err)
- {
- Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
- FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
- }
- }
- }
-
- /**
- * Action handler called to discard the template from the email body
- */
- public void discardTemplate(ActionEvent event)
- {
- this.body = this.automaticText;
- usingTemplate = null;
- }
-
/**
* @return Returns the notify listbox selection.
*/
@@ -605,70 +495,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
this.notify = notify;
}
- /**
- * @return Returns the email body text.
- */
- public String getBody()
- {
- return this.body;
- }
-
- /**
- * @param body The email body text to set.
- */
- public void setBody(String body)
- {
- this.body = body;
- }
-
- /**
- * @return Returns the email subject text.
- */
- public String getSubject()
- {
- return this.subject;
- }
-
- /**
- * @param subject The email subject text to set.
- */
- public void setSubject(String subject)
- {
- this.subject = subject;
- }
-
- /**
- * @return Returns the email template Id
- */
- public String getTemplate()
- {
- return this.template;
- }
-
- /**
- * @param template The email template to set.
- */
- public void setTemplate(String template)
- {
- this.template = template;
- }
-
- /**
- * @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;
- }
-
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
@@ -771,7 +597,7 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
personName}) );
// default the subject line to an informative message
- this.subject = buf.toString();
+ this.mailHelper.setSubject(buf.toString());
// add the rest of the automatic body text
buf.append("\r\n\r\n");
@@ -790,10 +616,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
buf.append(roleText);
- this.automaticText = buf.toString();
-
- // default the body content to this text
- this.body = this.automaticText;
+ // set the body content and default text to this text
+ this.mailHelper.setAutomaticText(buf.toString());
+ this.mailHelper.setBody(this.mailHelper.getAutomaticText());
}
return outcome;
@@ -825,7 +650,15 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
}
return outcome;
- }
+ }
+
+ /**
+ * @return TemplateMailHelperBean instance for this wizard
+ */
+ public TemplateMailHelperBean getMailHelper()
+ {
+ return this.mailHelper;
+ }
/**
* Simple wrapper class to represent a user/group and a role combination
diff --git a/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java b/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java
index 68228babe6..0100a7fff6 100644
--- a/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java
+++ b/source/java/org/alfresco/web/bean/wizard/NewUserWizard.java
@@ -462,12 +462,7 @@ public class NewUserWizard extends AbstractWizardBean
else
{
if (this.password.equals(this.confirm))
- {
- if (!this.personService.getUserNamesAreCaseSensitive())
- {
- this.userName = this.userName.toLowerCase();
- }
-
+ {
// create properties for Person type from submitted Form data
Map props = new HashMap(7, 1.0f);
props.put(ContentModel.PROP_USERNAME, this.userName);
diff --git a/source/java/org/alfresco/web/bean/wizard/WizardManager.java b/source/java/org/alfresco/web/bean/wizard/WizardManager.java
index f708413da4..a967648627 100644
--- a/source/java/org/alfresco/web/bean/wizard/WizardManager.java
+++ b/source/java/org/alfresco/web/bean/wizard/WizardManager.java
@@ -187,6 +187,18 @@ public class WizardManager
{
return Integer.toString(this.currentStep);
}
+
+ /**
+ * Returns the name of the current step, wizards should use
+ * the name of the step rather than the step number to discover
+ * the position as extra steps can be added via configuration.
+ *
+ * @return The name of the current step
+ */
+ public String getCurrentStepName()
+ {
+ return ((StepConfig)this.steps.get(this.currentStep-1)).getName();
+ }
/**
* Returns a list of UIListItems representing the steps of the wizard
diff --git a/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java b/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java
new file mode 100644
index 0000000000..c9c0b78964
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/workflow/CancelWorkflowDialog.java
@@ -0,0 +1,106 @@
+package org.alfresco.web.bean.workflow;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.bean.dialog.BaseDialogBean;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Bean implementation for the "Cancel Workflow" dialog
+ *
+ * @author gavinc
+ */
+public class CancelWorkflowDialog extends BaseDialogBean
+{
+ protected String workflowInstanceId;
+ protected WorkflowService workflowService;
+
+ private static final Log logger = LogFactory.getLog(CancelWorkflowDialog.class);
+
+ // ------------------------------------------------------------------------------
+ // Dialog implementation
+
+ @Override
+ public void init(Map parameters)
+ {
+ super.init(parameters);
+
+ // make sure the workflow instance id has been passed
+ this.workflowInstanceId = this.parameters.get("workflow-instance-id");
+ if (this.workflowInstanceId == null || this.workflowInstanceId.length() == 0)
+ {
+ throw new IllegalArgumentException("Cancel workflow dialog called without workflow instance id");
+ }
+ }
+
+ @Override
+ protected String finishImpl(FacesContext context, String outcome)
+ throws Exception
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Cancelling workflow with id: " + this.workflowInstanceId);
+
+ // cancel the workflow
+ this.workflowService.cancelWorkflow(this.workflowInstanceId);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Cancelled workflow with id: " + this.workflowInstanceId);
+
+ return outcome;
+ }
+
+ @Override
+ protected String getErrorMessageId()
+ {
+ return "error_cancel_workflow";
+ }
+
+ @Override
+ public boolean getFinishButtonDisabled()
+ {
+ return false;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Returns the confirmation to display to the user before deleting the content.
+ *
+ * @return The formatted message to display
+ */
+ public String getConfirmMessage()
+ {
+ String confirmMsg = Application.getMessage(FacesContext.getCurrentInstance(),
+ "cancel_workflow_confirm");
+
+ return MessageFormat.format(confirmMsg,
+ new Object[] {this.parameters.get("workflow-instance-name")});
+ }
+
+ /**
+ * Returns the workflow service instance
+ *
+ * @return WorkflowService instance
+ */
+ public WorkflowService getWorkflowService()
+ {
+ return workflowService;
+ }
+
+ /**
+ * Sets the workflow service to use
+ *
+ * @param workflowService The WorkflowService instance
+ */
+ public void setWorkflowService(WorkflowService workflowService)
+ {
+ this.workflowService = workflowService;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java
new file mode 100644
index 0000000000..05842b6c22
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/workflow/ManageWorkItemDialog.java
@@ -0,0 +1,476 @@
+package org.alfresco.web.bean.workflow;
+
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.ActionEvent;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.workflow.WorkflowModel;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowTransition;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.bean.dialog.BaseDialogBean;
+import org.alfresco.web.bean.repository.MapNode;
+import org.alfresco.web.bean.repository.Node;
+import org.alfresco.web.bean.repository.NodePropertyResolver;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.bean.repository.TransientNode;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.component.UIActionLink;
+import org.alfresco.web.ui.common.component.data.UIRichList;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Bean implementation for the "Manage WorkItem" dialog.
+ *
+ * @author gavinc
+ */
+public class ManageWorkItemDialog extends BaseDialogBean
+{
+ protected WorkflowService workflowService;
+ protected Node workItemNode;
+ protected WorkflowTask workItem;
+ protected WorkflowTransition[] transitions;
+ protected List resources;
+ protected WorkItemCompleteResolver completeResolver = new WorkItemCompleteResolver();
+ protected UIRichList packageItemsRichList;
+
+ protected static final String ID_PREFIX = "transition_";
+ protected static final String CLIENT_ID_PREFIX = "dialog:" + ID_PREFIX;
+
+ private static final Log logger = LogFactory.getLog(ManageWorkItemDialog.class);
+
+ // ------------------------------------------------------------------------------
+ // Dialog implementation
+
+ @Override
+ public void init(Map parameters)
+ {
+ super.init(parameters);
+
+ String taskId = this.parameters.get("id");
+ this.workItem = this.workflowService.getTaskById(taskId);
+
+ if (this.workItem != null)
+ {
+ // setup a transient node to represent the work item we're managing
+ WorkflowTaskDefinition taskDef = this.workItem.definition;
+ this.workItemNode = new TransientNode(taskDef.metadata.getName(),
+ "task_" + System.currentTimeMillis(), this.workItem.properties);
+ }
+ }
+
+ @Override
+ protected String finishImpl(FacesContext context, String outcome)
+ throws Exception
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Saving work item: " + this.workItemNode.getId());
+
+ // prepare the edited parameters for saving
+ Map params = WorkflowBean.prepareWorkItemParams(this.workItemNode);
+
+ // update the task with the updated parameters
+ this.workflowService.updateTask(this.workItem.id, params, null, null);
+
+ return outcome;
+ }
+
+ @Override
+ public List getAdditionalButtons()
+ {
+ List buttons = null;
+
+ if (this.workItem != null)
+ {
+ // get the transitions available from this work item and
+ // show them in the dialog as additional buttons
+ this.transitions = this.workItem.path.node.transitions;
+
+ if (this.transitions != null)
+ {
+ buttons = new ArrayList(this.transitions.length);
+
+ for (WorkflowTransition trans : this.transitions)
+ {
+ buttons.add(new DialogButtonConfig(ID_PREFIX + trans.title, trans.title, null,
+ "#{DialogManager.bean.transition}", "false", null));
+ }
+ }
+ }
+
+ return buttons;
+ }
+
+ @Override
+ public String getFinishButtonLabel()
+ {
+ return Application.getMessage(FacesContext.getCurrentInstance(), "save");
+ }
+
+ @Override
+ public boolean getFinishButtonDisabled()
+ {
+ return false;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Event handlers
+
+ @SuppressWarnings("unused")
+ public String transition()
+ {
+ String outcome = getDefaultFinishOutcome();
+
+ if (logger.isDebugEnabled())
+ logger.debug("Transitioning work item: " + this.workItemNode.getId());
+
+ // to find out which transition button was pressed we need
+ // to look for the button's id in the request parameters,
+ // the first non-null result is the button that was pressed.
+ FacesContext context = FacesContext.getCurrentInstance();
+ Map reqParams = context.getExternalContext().getRequestParameterMap();
+
+ String selectedTransition = null;
+ for (WorkflowTransition trans : this.transitions)
+ {
+ Object result = reqParams.get(CLIENT_ID_PREFIX + trans.title);
+ if (result != null)
+ {
+ // this was the button that was pressed
+ selectedTransition = trans.id;
+ break;
+ }
+ }
+
+ if (selectedTransition != null)
+ {
+ UserTransaction tx = null;
+
+ try
+ {
+ tx = Repository.getUserTransaction(context);
+ tx.begin();
+
+ // prepare the edited parameters for saving
+ Map params = WorkflowBean.prepareWorkItemParams(this.workItemNode);
+
+ // update the task with the updated parameters
+ this.workflowService.updateTask(this.workItem.id, params, null, null);
+
+ // signal the selected transition to the workflow task
+ this.workflowService.endTask(this.workItem.id, selectedTransition);
+
+ // commit the changes
+ tx.commit();
+
+ if (logger.isDebugEnabled())
+ logger.debug("Ended work item with transition: " + selectedTransition);
+ }
+ catch (Throwable e)
+ {
+ // rollback the transaction
+ try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
+ Utils.addErrorMessage(formatErrorMessage(e), e);
+ outcome = this.getErrorOutcome(e);
+ }
+ }
+
+ return outcome;
+ }
+
+ /**
+ * Removes an item from the workflow package
+ *
+ * @param event The event containing a reference to the item to remove
+ */
+ public void removePackageItem(ActionEvent event)
+ {
+ logger.info("remove package item: " + event);
+ }
+
+ /**
+ * Toggles the complete flag for a workflow package item
+ *
+ * @param event The event containing a reference to the item to toggle the status for
+ */
+ public void togglePackageItemComplete(ActionEvent event)
+ {
+ UserTransaction tx = null;
+ try
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ tx = Repository.getUserTransaction(context);
+ tx.begin();
+
+ UIActionLink link = (UIActionLink)event.getComponent();
+ Map params = link.getParameterMap();
+
+ // create the node ref for the item we are toggling
+ NodeRef nodeRef = new NodeRef(Repository.getStoreRef(),
+ (String)params.get("id"));
+
+ // get the existing list of completed items
+ List completedItems = (List)this.workItem.properties.get(
+ WorkflowModel.PROP_COMPLETED_ITEMS);
+
+ if (completedItems == null)
+ {
+ // if it doesn't exist yet create the list and add the noderef
+ completedItems = new ArrayList(1);
+ completedItems.add(nodeRef);
+ this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS,
+ (Serializable)completedItems);
+ }
+ else
+ {
+ if (completedItems.contains(nodeRef))
+ {
+ // the item is already in the list remove it
+ completedItems.remove(nodeRef);
+
+ // NOTE: There is a bug somwehere which causes the list to be
+ // returned as a byte array instead of a list if an empty
+ // list is persisted, therefore if the list is now empty
+ // set the completed items back to null
+ if (completedItems.size() == 0)
+ {
+ this.workItem.properties.put(WorkflowModel.PROP_COMPLETED_ITEMS, null);
+ }
+ }
+ else
+ {
+ // the noderef is not in the list yet so just add it
+ completedItems.add(nodeRef);
+ }
+ }
+
+ // update the task with the updated parameters
+ this.workflowService.updateTask(this.workItem.id, this.workItem.properties,
+ null, null);
+
+ // commit the transaction
+ tx.commit();
+
+ // reset the rich list if the change was successful
+ this.packageItemsRichList.setValue(null);
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
+ this.resources = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Sets the rich list being used for the workflow package items
+ *
+ * @param richList The rich list instance
+ */
+ public void setPackageItemsRichList(UIRichList richList)
+ {
+ this.packageItemsRichList = richList;
+ }
+
+ /**
+ * Returns the rich list being used for the workflow package items
+ *
+ * @return The rich list instance
+ */
+ public UIRichList getPackageItemsRichList()
+ {
+ return this.packageItemsRichList;
+ }
+
+ /**
+ * Returns the Node representing the work item
+ *
+ * @return The node
+ */
+ public Node getWorkItemNode()
+ {
+ return this.workItemNode;
+ }
+
+ /**
+ * Returns the action group the current task uses for the workflow package
+ *
+ * @return action group id
+ */
+ public String getPackageActionGroup()
+ {
+ return (String)this.workItem.properties.get(
+ WorkflowModel.PROP_PACKAGE_ACTION_GROUP);
+ }
+
+ /**
+ * Returns the action group the current task uses for each workflow package item
+ *
+ * @return action group id
+ */
+ public String getPackageItemActionGroup()
+ {
+ return (String)this.workItem.properties.get(
+ WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP);
+ }
+
+ /**
+ * Returns a list of resources associated with this work item
+ * i.e. the children of the workflow package
+ *
+ * @return The list of nodes
+ */
+ public List getResources()
+ {
+ NodeRef workflowPackage = null;
+ Serializable obj = this.workItem.properties.get(WorkflowModel.ASSOC_PACKAGE);
+ // TODO: remove this workaroud where JBPM may return a String and not the NodeRef
+ if (obj instanceof NodeRef)
+ {
+ workflowPackage = (NodeRef)obj;
+ }
+ else if (obj instanceof String)
+ {
+ workflowPackage = new NodeRef((String)obj);
+ }
+
+ this.resources = new ArrayList(4);
+
+ if (workflowPackage != null)
+ {
+ UserTransaction tx = null;
+ try
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ tx = Repository.getUserTransaction(context, true);
+ tx.begin();
+
+ if (logger.isDebugEnabled())
+ logger.debug("Found workflow package for work item '" +
+ this.workItem.id + "': " + workflowPackage );
+
+ List childRefs = this.nodeService.getChildAssocs(workflowPackage,
+ ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
+
+ for (ChildAssociationRef ref: childRefs)
+ {
+ // create our Node representation from the NodeRef
+ NodeRef nodeRef = ref.getChildRef();
+
+ if (this.nodeService.exists(nodeRef))
+ {
+ // find it's type so we can see if it's a node we are interested in
+ QName type = this.nodeService.getType(nodeRef);
+
+ // make sure the type is defined in the data dictionary
+ TypeDefinition typeDef = this.dictionaryService.getType(type);
+
+ if (typeDef != null)
+ {
+ // look for content nodes or links to content
+ // NOTE: folders within workflow packages are ignored for now
+ if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) ||
+ ContentModel.TYPE_FILELINK.equals(type))
+ {
+ // create our Node representation
+ MapNode node = new MapNode(nodeRef, this.nodeService, true);
+ this.browseBean.setupCommonBindingProperties(node);
+
+ // add property resolvers to show path information
+ node.addPropertyResolver("path", this.browseBean.resolverPath);
+ node.addPropertyResolver("displayPath", this.browseBean.resolverDisplayPath);
+
+ // add a property resolver to indicate whether the item has been completed or not
+ node.addPropertyResolver("completed", this.completeResolver);
+
+ this.resources.add(node);
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type);
+ }
+ }
+ }
+
+ // commit the transaction
+ tx.commit();
+ }
+ catch (Throwable err)
+ {
+ Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
+ FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
+ this.resources = Collections.emptyList();
+ try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
+ }
+ }
+ else if (logger.isDebugEnabled())
+ {
+ logger.debug("Failed to find workflow package for work item: " + this.workItem.id);
+ }
+
+ return this.resources;
+ }
+
+ /**
+ * Sets the workflow service to use
+ *
+ * @param workflowService
+ * WorkflowService instance
+ */
+ public void setWorkflowService(WorkflowService workflowService)
+ {
+ this.workflowService = workflowService;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
+
+ // ------------------------------------------------------------------------------
+ // Inner classes
+
+ /**
+ * Property resolver to determine if the given node has been flagged as complete
+ */
+ protected class WorkItemCompleteResolver implements NodePropertyResolver
+ {
+ public Object get(Node node)
+ {
+ String result = Application.getMessage(FacesContext.getCurrentInstance(), "no");
+
+ List completedItems = (List)workItem.properties.get(
+ WorkflowModel.PROP_COMPLETED_ITEMS);
+
+ if (completedItems != null && completedItems.size() > 0 &&
+ completedItems.contains(node.getNodeRef()))
+ {
+ result = Application.getMessage(FacesContext.getCurrentInstance(), "yes");
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java b/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java
new file mode 100644
index 0000000000..67f682e244
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/workflow/ReassignWorkItemDialog.java
@@ -0,0 +1,199 @@
+package org.alfresco.web.bean.workflow;
+
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.model.SelectItem;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.bean.dialog.BaseDialogBean;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.ui.common.SortableSelectItem;
+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;
+
+/**
+ * Bean implementation for the "Reassign Work Item" dialog
+ *
+ * @author gavinc
+ */
+public class ReassignWorkItemDialog extends BaseDialogBean
+{
+ protected String workItemId;
+
+ protected WorkflowService workflowService;
+ protected PersonService personService;
+
+ private static final Log logger = LogFactory.getLog(ReassignWorkItemDialog.class);
+
+ // ------------------------------------------------------------------------------
+ // Dialog implementation
+
+ @Override
+ public void init(Map parameters)
+ {
+ super.init(parameters);
+
+ this.workItemId = this.parameters.get("workitem-id");
+ if (this.workItemId == null || this.workItemId.length() == 0)
+ {
+ throw new IllegalArgumentException("Reassign workitem dialog called without task id");
+ }
+ }
+
+ @Override
+ protected String finishImpl(FacesContext context, String outcome)
+ throws Exception
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Reassigning work item with id: " + this.workItemId);
+
+ UIComponent picker = context.getViewRoot().findComponent("dialog:dialog-body:user-picker");
+
+ if (picker != null && picker instanceof UIGenericPicker)
+ {
+ UIGenericPicker userPicker = (UIGenericPicker)picker;
+ String[] user = userPicker.getSelectedResults();
+ if (user != null && user.length > 0)
+ {
+ // create a map to hold the new owner property then update the task
+ String userName = user[0];
+ Map params = new HashMap(1);
+ params.put(ContentModel.PROP_OWNER, userName);
+ this.workflowService.updateTask(this.workItemId, params, null, null);
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ logger.warn("Failed to find selected user, reassign was unsuccessful");
+ }
+ }
+ else
+ {
+ if (logger.isWarnEnabled())
+ logger.warn("Failed to find user-picker component, reassign was unsuccessful");
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("Reassigning work item with id: " + this.workItemId);
+
+ return outcome;
+ }
+
+ @Override
+ protected String getErrorMessageId()
+ {
+ return "error_reassign_workitem";
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Property accessed by the Generic Picker component.
+ *
+ * @return the array of filter options to show in the users/groups picker
+ */
+ public SelectItem[] getFilters()
+ {
+ ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
+ return new SelectItem[] {new SelectItem("0", bundle.getString("users"))};
+ }
+
+ /**
+ * Query callback method executed by the Generic Picker component.
+ * This method is part of the contract to the Generic Picker, it is up to the backing bean
+ * to execute whatever query is appropriate and return the results.
+ *
+ * @param filterIndex Index of the filter drop-down selection
+ * @param contains Text from the contains textbox
+ *
+ * @return An array of SelectItem objects containing the results to display in the picker.
+ */
+ public SelectItem[] pickerCallback(int filterIndex, String contains)
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ SelectItem[] items;
+
+ UserTransaction tx = null;
+ try
+ {
+ tx = Repository.getUserTransaction(context, true);
+ tx.begin();
+
+ // build xpath to match available User/Person objects
+ NodeRef peopleRef = personService.getPeopleContainer();
+ // NOTE: see SearcherComponentTest
+ String xpath = "*[like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "firstName, '%" + contains + "%', false)" +
+ " or " + "like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "lastName, '%" + contains + "%', false)]";
+
+ List nodes = searchService.selectNodes(
+ peopleRef,
+ xpath,
+ null,
+ this.namespaceService,
+ false);
+
+ items = new SelectItem[nodes.size()];
+ for (int index=0; index availableWorkflows;
+ protected Map workflows;
+ protected WorkflowService workflowService;
+ protected Node startTaskNode;
+ protected boolean nextButtonDisabled = false;
+
+ private static final Log logger = LogFactory.getLog(StartWorkflowWizard.class);
+
+ // ------------------------------------------------------------------------------
+ // Wizard implementation
+
+ @Override
+ public void init(Map parameters)
+ {
+ super.init(parameters);
+
+ // reset the selected workflow
+ if (this.availableWorkflows != null && this.availableWorkflows.size() > 0)
+ {
+ this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue();
+ }
+ else
+ {
+ this.selectedWorkflow = null;
+ }
+
+ this.startTaskNode = null;
+ }
+
+ @Override
+ protected String finishImpl(FacesContext context, String outcome)
+ throws Exception
+ {
+ // TODO: Deal with workflows that don't require any data
+
+ if (logger.isDebugEnabled())
+ logger.debug("Starting workflow: " + this.selectedWorkflow);
+
+ // prepare the parameters from the current state of the property sheet
+ Map params = WorkflowBean.prepareWorkItemParams(this.startTaskNode);
+
+ // create a workflow package for the attached items and add them
+ String itemToWorkflowId = this.parameters.get("item-to-workflow");
+ if (itemToWorkflowId != null && itemToWorkflowId.length() > 0)
+ {
+ // create the node ref for the item and determine its type
+ NodeRef itemToWorkflow = new NodeRef(Repository.getStoreRef(), itemToWorkflowId);
+ QName type = this.nodeService.getType(itemToWorkflow);
+
+ NodeRef workflowPackage = null;
+ if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) ||
+ this.dictionaryService.isSubClass(type, ContentModel.TYPE_FILELINK))
+ {
+ // create a workflow package and add the given item to workflow as a child
+ workflowPackage = this.workflowService.createPackage(null);
+ this.nodeService.addChild(workflowPackage, itemToWorkflow,
+ ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
+ QName.createValidLocalName((String)this.nodeService.getProperty(
+ itemToWorkflow, ContentModel.PROP_NAME))));
+ }
+
+ // add the workflow package to the parameter map
+ params.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage);
+ }
+
+ // setup the context for the workflow (this is the space the workflow was launched from)
+ Node workflowContext = this.navigator.getCurrentNode();
+ if (workflowContext != null)
+ {
+ params.put(WorkflowModel.PROP_CONTEXT, (Serializable)workflowContext.getNodeRef());
+ }
+
+ // start the workflow to get access to the start task
+ WorkflowPath path = this.workflowService.startWorkflow(this.selectedWorkflow, params);
+ if (path != null)
+ {
+ // extract the start task
+ List tasks = this.workflowService.getTasksForWorkflowPath(path.id);
+ if (tasks.size() == 1)
+ {
+ WorkflowTask startTask = tasks.get(0);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Found start task:" + startTask);
+
+ if (startTask.state == WorkflowTaskState.IN_PROGRESS)
+ {
+ // end the start task to trigger the first 'proper'
+ // task in the workflow
+ this.workflowService.endTask(startTask.id, null);
+ }
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("Started workflow: " + this.selectedWorkflow);
+ }
+
+ return outcome;
+ }
+
+ @Override
+ public String next()
+ {
+ String stepName = Application.getWizardManager().getCurrentStepName();
+
+ if ("options".equals(stepName) && this.startTaskNode == null)
+ {
+ // retrieve the start task for the selected workflow, get the task
+ // definition and create a transient node to allow the property
+ // sheet to collect the required data.
+
+ WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Selected workflow: "+ flowDef);
+
+ WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
+ if (taskDef != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Start task definition: " + taskDef);
+
+ // create an instance of a task from the data dictionary
+ this.startTaskNode = new TransientNode(taskDef.metadata.getName(),
+ "task_" + System.currentTimeMillis(), null);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean getNextButtonDisabled()
+ {
+ return this.nextButtonDisabled;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Returns the workflow selected by the user
+ *
+ * @return The selected workflow
+ */
+ public String getSelectedWorkflow()
+ {
+ return selectedWorkflow;
+ }
+
+ /**
+ * Sets the selected workflow
+ *
+ * @param selectedWorkflow The workflow selected
+ */
+ public void setSelectedWorkflow(String selectedWorkflow)
+ {
+ this.selectedWorkflow = selectedWorkflow;
+ }
+
+ /**
+ * Returns the Node representing the start task metadata required
+ *
+ * @return The Node for the start task
+ */
+ public Node getTaskMetadataNode()
+ {
+ return this.startTaskNode;
+ }
+
+ /**
+ * Returns the action group the current task uses for the workflow package
+ *
+ * @return action group id
+ */
+ public String getPackageActionGroup()
+ {
+ String actionGroup = null;
+
+ WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
+ WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
+ if (taskDef != null)
+ {
+ PropertyDefinition propDef = taskDef.metadata.getProperties().get(
+ WorkflowModel.PROP_PACKAGE_ACTION_GROUP);
+ if (propDef != null)
+ {
+ actionGroup = propDef.getDefaultValue();
+ }
+ }
+
+ return actionGroup;
+ }
+
+ /**
+ * Returns the action group the current task uses for each workflow package item
+ *
+ * @return action group id
+ */
+ public String getPackageItemActionGroup()
+ {
+ String actionGroup = null;
+
+ WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
+ WorkflowTaskDefinition taskDef = flowDef.startTaskDefinition;
+ if (taskDef != null)
+ {
+ PropertyDefinition propDef = taskDef.metadata.getProperties().get(
+ WorkflowModel.PROP_PACKAGE_ITEM_ACTION_GROUP);
+ if (propDef != null)
+ {
+ actionGroup = propDef.getDefaultValue();
+ }
+ }
+
+ return actionGroup;
+ }
+
+ /**
+ * @return Returns the summary data for the wizard.
+ */
+ public String getSummary()
+ {
+ ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
+
+ String workflowName = null;
+ for (SelectItem item : this.availableWorkflows)
+ {
+ if (item.getValue().equals(this.selectedWorkflow))
+ {
+ workflowName = item.getLabel();
+ break;
+ }
+ }
+
+ return buildSummary(
+ new String[] {bundle.getString("start_workflow")},
+ new String[] {workflowName});
+ }
+
+ /**
+ * Returns a list of workflows that can be started.
+ *
+ * @return List of SelectItem objects representing the workflows
+ */
+ public List getStartableWorkflows()
+ {
+ if (this.availableWorkflows == null)
+ {
+ this.availableWorkflows = new ArrayList(4);
+ this.workflows = new HashMap(4);
+
+ List workflowDefs = this.workflowService.getDefinitions();
+ for (WorkflowDefinition workflowDef : workflowDefs)
+ {
+ String label = workflowDef.title;
+ if (workflowDef.description != null && workflowDef.description.length() > 0)
+ {
+ label = label + " (" + workflowDef.description + ")";
+ }
+ this.availableWorkflows.add(new SelectItem(workflowDef.id, label));
+ this.workflows.put(workflowDef.id, workflowDef);
+ }
+
+ // set the initial selected workflow to the first in the list, unless there are no
+ // workflows, in which disable the next button
+ if (this.availableWorkflows.size() > 0)
+ {
+ this.selectedWorkflow = (String)this.availableWorkflows.get(0).getValue();
+ }
+ else
+ {
+ this.nextButtonDisabled = true;
+ }
+ }
+
+ return availableWorkflows;
+ }
+
+ /**
+ * Sets the workflow service to use
+ *
+ * @param workflowService WorkflowService instance
+ */
+ public void setWorkflowService(WorkflowService workflowService)
+ {
+ this.workflowService = workflowService;
+ }
+}
diff --git a/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java
new file mode 100644
index 0000000000..5368ca9be6
--- /dev/null
+++ b/source/java/org/alfresco/web/bean/workflow/WorkflowBean.java
@@ -0,0 +1,279 @@
+package org.alfresco.web.bean.workflow;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.workflow.WorkflowModel;
+import org.alfresco.service.cmr.repository.AssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.cmr.workflow.WorkflowTransition;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.ISO9075;
+import org.alfresco.web.app.Application;
+import org.alfresco.web.bean.repository.Node;
+import org.alfresco.web.bean.repository.Repository;
+import org.alfresco.web.bean.repository.TransientMapNode;
+import org.alfresco.web.bean.repository.User;
+import org.alfresco.web.ui.common.Utils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Managed bean used for handling workflow related features
+ *
+ * @author gavinc
+ */
+public class WorkflowBean
+{
+ protected NodeService nodeService;
+ protected WorkflowService workflowService;
+ protected List workItems;
+ protected List completedWorkItems;
+
+ private static final Log logger = LogFactory.getLog(WorkflowBean.class);
+
+ // ------------------------------------------------------------------------------
+ // Bean Getters and Setters
+
+ /**
+ * Returns a list of nodes representing the to do work items the
+ * current user has.
+ *
+ * @return List of to do work items
+ */
+ public List getWorkItemsToDo()
+ {
+ // get the current username
+ FacesContext context = FacesContext.getCurrentInstance();
+ User user = Application.getCurrentUser(context);
+ String userName = ISO9075.encode(user.getUserName());
+
+ UserTransaction tx = null;
+ try
+ {
+ tx = Repository.getUserTransaction(context, true);
+ tx.begin();
+
+ // get the current in progress tasks for the current user
+ List tasks = this.workflowService.getAssignedTasks(
+ userName, WorkflowTaskState.IN_PROGRESS);
+
+ // create a list of transient nodes to represent
+ this.workItems = new ArrayList(tasks.size());
+ for (WorkflowTask task : tasks)
+ {
+ Node node = createWorkItem(task);
+ this.workItems.add(node);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Added to do work item: " + node);
+ }
+
+ // commit the changes
+ tx.commit();
+ }
+ catch (Throwable e)
+ {
+ // rollback the transaction
+ try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
+ Utils.addErrorMessage("Failed to get to do work items: " + e.toString(), e);
+ }
+
+ return this.workItems;
+ }
+
+ /**
+ * Returns a list of nodes representing the completed work items the
+ * current user has.
+ *
+ * @return List of completed work items
+ */
+ public List getWorkItemsCompleted()
+ {
+ // get the current username
+ FacesContext context = FacesContext.getCurrentInstance();
+ User user = Application.getCurrentUser(context);
+ String userName = ISO9075.encode(user.getUserName());
+
+ UserTransaction tx = null;
+ try
+ {
+ tx = Repository.getUserTransaction(context, true);
+ tx.begin();
+
+ // get the current in progress tasks for the current user
+ List tasks = this.workflowService.getAssignedTasks(
+ userName, WorkflowTaskState.COMPLETED);
+
+ // create a list of transient nodes to represent
+ this.completedWorkItems = new ArrayList(tasks.size());
+ for (WorkflowTask task : tasks)
+ {
+ Node node = createWorkItem(task);
+ this.completedWorkItems.add(node);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Added completed work item: " + node);
+ }
+
+ // commit the changes
+ tx.commit();
+ }
+ catch (Throwable e)
+ {
+ // rollback the transaction
+ try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
+ Utils.addErrorMessage("Failed to get completed work items: " + e.toString(), e);
+ }
+
+ return this.completedWorkItems;
+ }
+
+ /**
+ * Sets the workflow service to use
+ *
+ * @param workflowService WorkflowService instance
+ */
+ public void setWorkflowService(WorkflowService workflowService)
+ {
+ this.workflowService = workflowService;
+ }
+
+ /**
+ * Sets the node service to use
+ *
+ * @param nodeService NodeService instance
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ // ------------------------------------------------------------------------------
+ // Helper methods
+
+ public static Map prepareWorkItemParams(Node node)
+ {
+ Map params = new HashMap();
+
+ // marshal the properties and associations captured by the property sheet
+ // back into a Map to pass to the workflow service
+
+ // go through all the properties in the transient node and add them to
+ // params map
+ Map props = node.getProperties();
+ for (String propName : props.keySet())
+ {
+ QName propQName = Repository.resolveToQName(propName);
+ params.put(propQName, (Serializable)props.get(propName));
+ }
+
+ // go through any associations that have been added to the start task
+ // and build a list of NodeRefs representing the targets
+ Map> assocs = node.getAddedAssociations();
+ for (String assocName : assocs.keySet())
+ {
+ QName assocQName = Repository.resolveToQName(assocName);
+
+ // get the associations added and create list of targets
+ Map addedAssocs = assocs.get(assocName);
+ List targets = new ArrayList(addedAssocs.size());
+ for (AssociationRef assoc : addedAssocs.values())
+ {
+ targets.add(assoc.getTargetRef());
+ }
+
+ // add the targets for this particular association
+ params.put(assocQName, (Serializable)targets);
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("Prepared parameters: " + params);
+
+ return params;
+ }
+
+ /**
+ * Creates and populates a TransientNode to represent the given
+ * workflow task from the repository workflow engine
+ *
+ * @param task The task to create a representation of
+ */
+ protected TransientMapNode createWorkItem(WorkflowTask task)
+ {
+ // get the type of the task
+ WorkflowTaskDefinition taskDef = task.definition;
+
+ // create the basic transient node
+ TransientMapNode node = new TransientMapNode(taskDef.metadata.getName(),
+ task.title, task.properties);
+
+ // add properties for the other useful metadata
+ node.getProperties().put(ContentModel.PROP_NAME.toString(), task.title);
+ node.getProperties().put("type", taskDef.metadata.getTitle());
+ node.getProperties().put("id", task.id);
+
+ // add the name of the source space (if there is one)
+ // TODO: remove this workaroud where JBPM may return a String and not the NodeRef
+ Serializable obj = task.properties.get(WorkflowModel.PROP_CONTEXT);
+ NodeRef context = null;
+ if (obj instanceof NodeRef)
+ {
+ context = (NodeRef)obj;
+ }
+ else if (obj instanceof String)
+ {
+ context = new NodeRef((String)obj);
+ }
+
+ if (context != null)
+ {
+ String name = Repository.getNameForNode(this.nodeService, context);
+ node.getProperties().put("sourceSpaceName", name);
+ node.getProperties().put("sourceSpaceId", context.getId());
+ }
+
+ // add extra properties for completed tasks
+ if (task.state.equals(WorkflowTaskState.COMPLETED))
+ {
+ // add the outcome label for any completed task
+ String outcome = null;
+ String transition = (String)task.properties.get(WorkflowModel.PROP_OUTCOME);
+ if (transition != null)
+ {
+ WorkflowTransition[] transitions = task.definition.node.transitions;
+ for (WorkflowTransition trans : transitions)
+ {
+ if (trans.id.equals(transition))
+ {
+ outcome = trans.title;
+ break;
+ }
+ }
+
+ if (outcome != null)
+ {
+ node.getProperties().put("outcome", outcome);
+ }
+ }
+
+ // add the workflow instance id and name this taks belongs to
+ node.getProperties().put("workflowInstanceId", task.path.instance.id);
+ node.getProperties().put("workflowInstanceName", task.path.instance.definition.title);
+ }
+
+ return node;
+ }
+}
diff --git a/source/java/org/alfresco/web/config/ActionsConfigElement.java b/source/java/org/alfresco/web/config/ActionsConfigElement.java
index b57336ff36..e4c835f6b4 100644
--- a/source/java/org/alfresco/web/config/ActionsConfigElement.java
+++ b/source/java/org/alfresco/web/config/ActionsConfigElement.java
@@ -229,6 +229,7 @@ public class ActionsConfigElement extends ConfigElementAdapter
public String Action;
public String Href;
public String Target;
+ public String Script;
public String Onclick;
}
diff --git a/source/java/org/alfresco/web/config/ActionsElementReader.java b/source/java/org/alfresco/web/config/ActionsElementReader.java
index 6c1fce52d1..7ebd0ce8cc 100644
--- a/source/java/org/alfresco/web/config/ActionsElementReader.java
+++ b/source/java/org/alfresco/web/config/ActionsElementReader.java
@@ -50,6 +50,7 @@ public class ActionsElementReader implements ConfigElementReader
public static final String ELEMENT_ONCLICK = "onclick";
public static final String ELEMENT_HREF = "href";
public static final String ELEMENT_TARGET = "target";
+ public static final String ELEMENT_SCRIPT = "script";
public static final String ELEMENT_PARAMS = "params";
public static final String ELEMENT_PARAM = "param";
public static final String ATTRIBUTE_ID = "id";
@@ -252,6 +253,7 @@ public class ActionsElementReader implements ConfigElementReader
actionDef.TooltipMsg = actionElement.elementTextTrim(ELEMENT_TOOLTIPMSG);
actionDef.Href = actionElement.elementTextTrim(ELEMENT_HREF);
actionDef.Target = actionElement.elementTextTrim(ELEMENT_TARGET);
+ actionDef.Script = actionElement.elementTextTrim(ELEMENT_SCRIPT);
actionDef.Action = actionElement.elementTextTrim(ELEMENT_ACTION);
actionDef.ActionListener = actionElement.elementTextTrim(ELEMENT_ACTIONLISTENER);
actionDef.Onclick = actionElement.elementTextTrim(ELEMENT_ONCLICK);
diff --git a/source/java/org/alfresco/web/config/ClientConfigElement.java b/source/java/org/alfresco/web/config/ClientConfigElement.java
index 4c75bfcc8d..0f6a3e216e 100644
--- a/source/java/org/alfresco/web/config/ClientConfigElement.java
+++ b/source/java/org/alfresco/web/config/ClientConfigElement.java
@@ -40,6 +40,7 @@ public class ClientConfigElement extends ConfigElementAdapter
private String editLinkType = "http";
private String homeSpacePermission = null;
private boolean ajaxEnabled = false;
+ private String initialLocation = null;
/**
* Default Constructor
@@ -145,6 +146,17 @@ public class ClientConfigElement extends ConfigElementAdapter
combinedElement.setFromEmailAddress(newElement.getFromEmailAddress());
}
+ if (newElement.isAjaxEnabled() != combinedElement.isAjaxEnabled())
+ {
+ combinedElement.setAjaxEnabled(newElement.isAjaxEnabled());
+ }
+
+ if (newElement.getInitialLocation() != null &&
+ newElement.getInitialLocation().equals(combinedElement.getInitialLocation()) == false)
+ {
+ combinedElement.setInitialLocation(newElement.getInitialLocation());
+ }
+
return combinedElement;
}
@@ -348,4 +360,20 @@ public class ClientConfigElement extends ConfigElementAdapter
{
this.ajaxEnabled = ajaxEnabled;
}
+
+ /**
+ * @return Returns the default initial location for the user.
+ */
+ public String getInitialLocation()
+ {
+ return this.initialLocation;
+ }
+
+ /**
+ * @param initialLocation The initial location to set.
+ */
+ /*package*/ void setInitialLocation(String initialLocation)
+ {
+ this.initialLocation = initialLocation;
+ }
}
diff --git a/source/java/org/alfresco/web/config/ClientElementReader.java b/source/java/org/alfresco/web/config/ClientElementReader.java
index 00feb8377f..43d25005ee 100644
--- a/source/java/org/alfresco/web/config/ClientElementReader.java
+++ b/source/java/org/alfresco/web/config/ClientElementReader.java
@@ -40,6 +40,7 @@ public class ClientElementReader implements ConfigElementReader
public static final String ELEMENT_FROMEMAILADDRESS = "from-email-address";
public static final String ELEMENT_SHELFVISIBLE = "shelf-visible";
public static final String ELEMENT_AJAX_ENABLED = "ajax-enabled";
+ public static final String ELEMENT_INITIALLOCATION = "initial-location";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
@@ -144,6 +145,13 @@ public class ClientElementReader implements ConfigElementReader
{
configElement.setAjaxEnabled(Boolean.parseBoolean(ajaxEnabled.getTextTrim()));
}
+
+ // get the initial location
+ Element initialLocation = element.element(ELEMENT_INITIALLOCATION);
+ if (initialLocation != null)
+ {
+ configElement.setInitialLocation(initialLocation.getTextTrim());
+ }
}
return configElement;
diff --git a/source/java/org/alfresco/web/config/DashboardsConfigElement.java b/source/java/org/alfresco/web/config/DashboardsConfigElement.java
index d35e0256e5..83ed1a8039 100644
--- a/source/java/org/alfresco/web/config/DashboardsConfigElement.java
+++ b/source/java/org/alfresco/web/config/DashboardsConfigElement.java
@@ -148,5 +148,6 @@ public class DashboardsConfigElement extends ConfigElementAdapter
public String Description;
public String DescriptionId;
public String JSPPage;
+ public String ConfigJSPPage;
}
}
diff --git a/source/java/org/alfresco/web/config/DashboardsElementReader.java b/source/java/org/alfresco/web/config/DashboardsElementReader.java
index b9e3e08079..e532ec8486 100644
--- a/source/java/org/alfresco/web/config/DashboardsElementReader.java
+++ b/source/java/org/alfresco/web/config/DashboardsElementReader.java
@@ -46,6 +46,7 @@ public class DashboardsElementReader implements ConfigElementReader
public static final String ATTR_LABELID = "label-id";
public static final String ATTR_DESCRIPTIONID = "description-id";
public static final String ATTR_JSP = "jsp";
+ public static final String ATTR_CONFIGJSP = "config-jsp";
public static final String ATTR_ALLOWNARROW = "allow-narrow";
/**
@@ -165,6 +166,7 @@ public class DashboardsElementReader implements ConfigElementReader
def.AllowNarrow = Boolean.parseBoolean(allowNarrow);
}
def.JSPPage = getMandatoryDashletAttributeValue(config, ATTR_JSP);
+ def.ConfigJSPPage = config.attributeValue(ATTR_CONFIGJSP);
String label = config.attributeValue(ATTR_LABEL);
String labelId = config.attributeValue(ATTR_LABELID);
if ((label == null || label.length() == 0) && (labelId == null || labelId.length() == 0))
diff --git a/source/java/org/alfresco/web/config/DialogsConfigElement.java b/source/java/org/alfresco/web/config/DialogsConfigElement.java
index e9298ce86c..677440b564 100644
--- a/source/java/org/alfresco/web/config/DialogsConfigElement.java
+++ b/source/java/org/alfresco/web/config/DialogsConfigElement.java
@@ -131,12 +131,15 @@ public class DialogsConfigElement extends ConfigElementAdapter
protected String description;
protected String descriptionId;
protected String errorMsgId = "error_dialog";
+ protected boolean isOKButtonVisible = true;
+ protected List buttons;
public DialogConfig(String name, String page, String bean,
String actionsConfigId, String icon,
String title, String titleId,
String description, String descriptionId,
- String errorMsgId)
+ String errorMsgId, boolean isOKButtonVisible,
+ List buttons)
{
// check the mandatory parameters are present
ParameterCheck.mandatoryString("name", name);
@@ -152,6 +155,8 @@ public class DialogsConfigElement extends ConfigElementAdapter
this.titleId = titleId;
this.description = description;
this.descriptionId = descriptionId;
+ this.isOKButtonVisible = isOKButtonVisible;
+ this.buttons = buttons;
if (errorMsgId != null && errorMsgId.length() > 0)
{
@@ -209,6 +214,16 @@ public class DialogsConfigElement extends ConfigElementAdapter
return this.errorMsgId;
}
+ public boolean isOKButtonVisible()
+ {
+ return this.isOKButtonVisible;
+ }
+
+ public List getButtons()
+ {
+ return this.buttons;
+ }
+
/**
* @see java.lang.Object#toString()
*/
@@ -225,7 +240,98 @@ public class DialogsConfigElement extends ConfigElementAdapter
buffer.append(" titleId=").append(this.titleId);
buffer.append(" description=").append(this.description);
buffer.append(" descriptionId=").append(this.descriptionId);
- buffer.append(" errorMsgId=").append(this.errorMsgId).append(")");
+ buffer.append(" errorMsgId=").append(this.errorMsgId);
+ buffer.append(" isOKButtonVisible=").append(this.isOKButtonVisible);
+ buffer.append(" buttons=").append(this.buttons).append(")");
+ return buffer.toString();
+ }
+ }
+
+ /**
+ * Inner class representing the configuration for an additional
+ * dialog button.
+ *
+ * @author gavinc
+ */
+ public static class DialogButtonConfig
+ {
+ private String id;
+ private String label;
+ private String labelId;
+ private String action;
+ private String disabled;
+ private String onclick;
+
+ public DialogButtonConfig(String id, String label, String labelId,
+ String action, String disabled, String onclick)
+ {
+ this.id = id;
+ this.label = label;
+ this.labelId = labelId;
+ this.action = action;
+ this.disabled = disabled;
+ this.onclick = onclick;
+
+ if ((this.label == null || this.label.length() == 0) &&
+ (this.labelId == null || this.labelId.length() == 0))
+ {
+ throw new ConfigException("A dialog button needs to have a label or a label-id");
+ }
+
+ if (this.action == null || this.action.length() == 0)
+ {
+ throw new ConfigException("A dialog button requires an action");
+ }
+ else if (this.action.startsWith("#{") == false)
+ {
+ throw new ConfigException("The action for a dialog button must be a method binding expression, '"
+ + this.action + "' is not!");
+ }
+ }
+
+ public String getAction()
+ {
+ return action;
+ }
+
+ public String getDisabled()
+ {
+ return disabled;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public String getLabel()
+ {
+ return label;
+ }
+
+ public String getLabelId()
+ {
+ return labelId;
+ }
+
+ public String getOnclick()
+ {
+ return onclick;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder(super.toString());
+ buffer.append(" (id=").append(this.id);
+ buffer.append(" label=").append(this.label);
+ buffer.append(" label-id=").append(this.labelId);
+ buffer.append(" action=").append(this.action);
+ buffer.append(" disabled=").append(this.disabled);
+ buffer.append(" onclick=").append(this.onclick).append(")");
return buffer.toString();
}
}
diff --git a/source/java/org/alfresco/web/config/DialogsElementReader.java b/source/java/org/alfresco/web/config/DialogsElementReader.java
index f0fc6f4949..7df5472757 100644
--- a/source/java/org/alfresco/web/config/DialogsElementReader.java
+++ b/source/java/org/alfresco/web/config/DialogsElementReader.java
@@ -16,11 +16,14 @@
*/
package org.alfresco.web.config;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
import org.dom4j.Element;
/**
@@ -32,6 +35,8 @@ public class DialogsElementReader implements ConfigElementReader
{
public static final String ELEMENT_DIALOGS = "dialogs";
public static final String ELEMENT_DIALOG = "dialog";
+ public static final String ELEMENT_BUTTONS = "buttons";
+ public static final String ELEMENT_BUTTON = "button";
public static final String ATTR_NAME = "name";
public static final String ATTR_PAGE = "page";
public static final String ATTR_MANAGED_BEAN = "managed-bean";
@@ -42,10 +47,18 @@ public class DialogsElementReader implements ConfigElementReader
public static final String ATTR_DESCRIPTION = "description";
public static final String ATTR_DESCRIPTION_ID = "description-id";
public static final String ATTR_ERROR_MSG_ID = "error-message-id";
+ public static final String ATTR_SHOW_OK_BUTTON = "show-ok-button";
+ public static final String ATTR_ID = "id";
+ public static final String ATTR_LABEL = "label";
+ public static final String ATTR_LABEL_ID = "label-id";
+ public static final String ATTR_ACTION = "action";
+ public static final String ATTR_DISABLED = "disabled";
+ public static final String ATTR_ONCLICK = "onclick";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
+ @SuppressWarnings("unchecked")
public ConfigElement parse(Element element)
{
DialogsConfigElement configElement = null;
@@ -62,7 +75,7 @@ public class DialogsElementReader implements ConfigElementReader
configElement = new DialogsConfigElement();
- // go through the items to show
+ // go through the dialogs
Iterator items = element.elementIterator(ELEMENT_DIALOG);
while (items.hasNext())
{
@@ -78,10 +91,20 @@ public class DialogsElementReader implements ConfigElementReader
String description = item.attributeValue(ATTR_DESCRIPTION);
String descriptionId = item.attributeValue(ATTR_DESCRIPTION_ID);
String errorMsgId = item.attributeValue(ATTR_ERROR_MSG_ID);
+ String showOK = item.attributeValue(ATTR_SHOW_OK_BUTTON);
+
+ boolean isOKButtonVisible = true;
+ if (showOK != null)
+ {
+ isOKButtonVisible = Boolean.parseBoolean(showOK);
+ }
+
+ // parse any buttons that may be present
+ List buttons = parseButtons(item);
DialogsConfigElement.DialogConfig cfg = new DialogsConfigElement.DialogConfig(
name, page, bean, actions, icon, title, titleId, description,
- descriptionId, errorMsgId);
+ descriptionId, errorMsgId, isOKButtonVisible, buttons);
configElement.addDialog(cfg);
}
@@ -90,4 +113,44 @@ public class DialogsElementReader implements ConfigElementReader
return configElement;
}
+ /**
+ * Retrieve the configuration for additional buttons.
+ *
+ * @param dialog The dialog XML element
+ * @return List of configured buttons
+ */
+ @SuppressWarnings("unchecked")
+ protected List parseButtons(Element dialog)
+ {
+ List buttons = null;
+
+ // iterate over any configured buttons
+ Element buttonsConfig = dialog.element(ELEMENT_BUTTONS);
+ if (buttonsConfig != null)
+ {
+ buttons = new ArrayList(4);
+
+ Iterator children = buttonsConfig.elementIterator(ELEMENT_BUTTON);
+ while (children.hasNext())
+ {
+ Element button = children.next();
+
+ String id = button.attributeValue(ATTR_ID);
+ String label = button.attributeValue(ATTR_LABEL);
+ String labelId = button.attributeValue(ATTR_LABEL_ID);
+ String action = button.attributeValue(ATTR_ACTION);
+ String disabled = button.attributeValue(ATTR_DISABLED);
+ String onclick = button.attributeValue(ATTR_ONCLICK);
+
+ // create the button config object
+ DialogButtonConfig btnCfg = new DialogButtonConfig(id, label,
+ labelId, action, disabled, onclick);
+
+ // add the button to the list
+ buttons.add(btnCfg);
+ }
+ }
+
+ return buttons;
+ }
}
diff --git a/source/java/org/alfresco/web/config/PropertySheetConfigElement.java b/source/java/org/alfresco/web/config/PropertySheetConfigElement.java
index 5b556071fe..bd0fca32c9 100644
--- a/source/java/org/alfresco/web/config/PropertySheetConfigElement.java
+++ b/source/java/org/alfresco/web/config/PropertySheetConfigElement.java
@@ -32,9 +32,6 @@ import org.alfresco.config.element.ConfigElementAdapter;
*/
public class PropertySheetConfigElement extends ConfigElementAdapter
{
- // TODO: Currently this object just deals with properties and associations to show,
- // in the future it will also deal with properties and associations to hide.
-
public static final String CONFIG_ELEMENT_ID = "property-sheet";
protected Map items = new LinkedHashMap(8, 10f);
@@ -193,6 +190,24 @@ public class PropertySheetConfigElement extends ConfigElementAdapter
converter, inView, inEdit, compGenerator));
}
+ /**
+ * Adds a separator
+ *
+ * @param name The name of the separator
+ * @param displayLabel Display label to use for the separator
+ * @param displayLabelId Display label message id to use for the separator
+ * @param inView Sets whether the separator should be shown when the property
+ * sheet is in view mode
+ * @param inEdit Sets whether the separator should be shown when the property
+ * sheet is in edit mode
+ * @param compGenerator The name of a bean that can be used as a component generator
+ */
+ /*package*/ void addSeparator(String name, String displayLabel, String displayLabelId,
+ String inView, String inEdit, String compGenerator)
+ {
+ addItem(new SeparatorConfig(name, displayLabel, displayLabelId, inView, inEdit, compGenerator));
+ }
+
/**
* @return Returns a map of the all the items
*/
@@ -266,6 +281,12 @@ public class PropertySheetConfigElement extends ConfigElementAdapter
boolean readOnly, String converter, String inView, String inEdit,
String compGenerator, String ignoreIfMissing)
{
+ // check we have a name
+ if (name == null || name.length() == 0)
+ {
+ throw new ConfigException("You must specify a name for a proprty sheet item");
+ }
+
this.name = name;
this.displayLabel = displayLabel;
this.displayLabelId = displayLabelId;
@@ -420,4 +441,17 @@ public class PropertySheetConfigElement extends ConfigElementAdapter
inView, inEdit, compGenerator, null);
}
}
+
+ /**
+ * Inner class to represent a configured separator
+ */
+ public class SeparatorConfig extends ItemConfig
+ {
+ public SeparatorConfig(String name, String displayLabel, String displayLabelId,
+ String inView, String inEdit, String compGenerator)
+ {
+ super(name, displayLabel, displayLabelId, false, null,
+ inView, inEdit, compGenerator, null);
+ }
+ }
}
diff --git a/source/java/org/alfresco/web/config/PropertySheetElementReader.java b/source/java/org/alfresco/web/config/PropertySheetElementReader.java
index bdbf413ded..e97bd6669c 100644
--- a/source/java/org/alfresco/web/config/PropertySheetElementReader.java
+++ b/source/java/org/alfresco/web/config/PropertySheetElementReader.java
@@ -34,6 +34,7 @@ public class PropertySheetElementReader implements ConfigElementReader
public static final String ELEMENT_SHOW_PROPERTY = "show-property";
public static final String ELEMENT_SHOW_ASSOC = "show-association";
public static final String ELEMENT_SHOW_CHILD_ASSOC = "show-child-association";
+ public static final String ELEMENT_SEPARATOR = "separator";
public static final String ATTR_NAME = "name";
public static final String ATTR_DISPLAY_LABEL = "display-label";
public static final String ATTR_DISPLAY_LABEL_ID = "display-label-id";
@@ -96,6 +97,10 @@ public class PropertySheetElementReader implements ConfigElementReader
configElement.addChildAssociation(propName, label, labelId, readOnly, converter,
inView, inEdit, compGenerator);
}
+ else if (ELEMENT_SEPARATOR.equals(item.getName()))
+ {
+ configElement.addSeparator(propName, label, labelId, inView, inEdit, compGenerator);
+ }
}
}
diff --git a/source/java/org/alfresco/web/ui/common/ComponentConstants.java b/source/java/org/alfresco/web/ui/common/ComponentConstants.java
index 062a8afc89..4f7e6d3d1b 100644
--- a/source/java/org/alfresco/web/ui/common/ComponentConstants.java
+++ b/source/java/org/alfresco/web/ui/common/ComponentConstants.java
@@ -23,6 +23,7 @@ public final class ComponentConstants
{
public static final String JAVAX_FACES_INPUT = "javax.faces.Input";
public static final String JAVAX_FACES_TEXT = "javax.faces.Text";
+ public static final String JAVAX_FACES_TEXTAREA = "javax.faces.Textarea";
public static final String JAVAX_FACES_OUTPUT = "javax.faces.Output";
public static final String JAVAX_FACES_GRID = "javax.faces.Grid";
public static final String JAVAX_FACES_PANEL = "javax.faces.Panel";
@@ -31,6 +32,7 @@ public final class ComponentConstants
public static final String JAVAX_FACES_GRAPHIC = "javax.faces.Graphic";
public static final String JAVAX_FACES_PARAMETER = "javax.faces.Parameter";
public static final String JAVAX_FACES_MENU = "javax.faces.Menu";
+ public static final String JAVAX_FACES_BUTTON = "javax.faces.Button";
/**
* Private constructor
diff --git a/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java b/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java
index b8c74892f9..07a5ad9bb5 100644
--- a/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java
+++ b/source/java/org/alfresco/web/ui/common/component/UIGenericPicker.java
@@ -67,6 +67,7 @@ public class UIGenericPicker extends UICommand
private Boolean showContains = null;
private Boolean showAddButton = null;
private Boolean filterRefresh = null;
+ private Boolean multiSelect = null;
private String addButtonLabel;
private Integer width = null;
private Integer height = null;
@@ -118,6 +119,7 @@ public class UIGenericPicker extends UICommand
currentResults = (SelectItem[])values[11];
filters = (SelectItem[])values[12];
filterRefresh = (Boolean)values[13];
+ multiSelect = (Boolean)values[14];
}
/**
@@ -125,7 +127,7 @@ public class UIGenericPicker extends UICommand
*/
public Object saveState(FacesContext context)
{
- Object values[] = new Object[14];
+ Object values[] = new Object[15];
// standard component attributes are saved by the super class
values[0] = super.saveState(context);
values[1] = showFilter;
@@ -141,6 +143,7 @@ public class UIGenericPicker extends UICommand
values[11] = currentResults;
values[12] = filters;
values[13] = filterRefresh;
+ values[14] = multiSelect;
return (values);
}
@@ -221,7 +224,7 @@ public class UIGenericPicker extends UICommand
{
// use reflection to execute the query callback method and retrieve results
Object result = callback.invoke(getFacesContext(), new Object[] {
- this.filterIndex, this.contains});
+ this.filterIndex, this.contains.trim()});
if (result instanceof SelectItem[])
{
@@ -354,7 +357,14 @@ public class UIGenericPicker extends UICommand
out.write(Integer.toString(getHeight()));
out.write("px' name='");
out.write(clientId + FIELD_RESULTS);
- out.write("' multiple>");
+ out.write("' id='");
+ out.write(clientId + FIELD_RESULTS);
+ out.write("'");
+ if (getMultiSelect() == true)
+ {
+ out.write(" multiple");
+ }
+ out.write(">");
// results
if (currentResults != null)
@@ -530,6 +540,28 @@ public class UIGenericPicker extends UICommand
this.filterRefresh = Boolean.valueOf(filterRefresh);
}
+ /**
+ * @return true if multi select should be enabled.
+ */
+ public boolean getMultiSelect()
+ {
+ ValueBinding vb = getValueBinding("multiSelect");
+ if (vb != null)
+ {
+ this.multiSelect = (Boolean)vb.getValue(getFacesContext());
+ }
+
+ return multiSelect != null ? multiSelect.booleanValue() : true;
+ }
+
+ /**
+ * @param multiSelect Flag to determine whether multi select is enabled
+ */
+ public void setMultiSelect(boolean multiSelect)
+ {
+ this.multiSelect = Boolean.valueOf(multiSelect);
+ }
+
/**
* @return Returns the width.
*/
@@ -633,6 +665,7 @@ public class UIGenericPicker extends UICommand
/**
* Class representing the an action relevant to the Generic Selector component.
*/
+ @SuppressWarnings("serial")
public static class PickerEvent extends ActionEvent
{
public PickerEvent(UIComponent component, int action, int filterIndex, String contains, String[] results)
diff --git a/source/java/org/alfresco/web/ui/common/component/UIPanel.java b/source/java/org/alfresco/web/ui/common/component/UIPanel.java
index 6d12cafcd2..7a5842fc68 100644
--- a/source/java/org/alfresco/web/ui/common/component/UIPanel.java
+++ b/source/java/org/alfresco/web/ui/common/component/UIPanel.java
@@ -101,6 +101,10 @@ public class UIPanel extends UICommand
// determine whether we have any adornments
String label = getLabel();
+ if (label != null)
+ {
+ label = Utils.encode(label);
+ }
if (label != null || isProgressive() == true || titleComponent != null)
{
this.hasAdornments = true;
@@ -156,11 +160,11 @@ public class UIPanel extends UICommand
if (isExpanded() == true)
{
- out.write(Utils.buildImageTag(context, WebResources.IMAGE_EXPANDED, 11, 11, ""));
+ out.write(Utils.buildImageTag(context, WebResources.IMAGE_EXPANDED, 11, 11, label));
}
else
{
- out.write(Utils.buildImageTag(context, WebResources.IMAGE_COLLAPSED, 11, 11, ""));
+ out.write(Utils.buildImageTag(context, WebResources.IMAGE_COLLAPSED, 11, 11, label));
}
out.write(" ");
@@ -174,11 +178,11 @@ public class UIPanel extends UICommand
Utils.outputAttribute(out, getAttributes().get("styleClass"), "class");
out.write('>');
- out.write(Utils.encode(label));
+ out.write(label); // already encoded above
out.write("");
}
-
+
if (this.hasAdornments)
{
out.write("");
diff --git a/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java
index 85cdb404b5..0d0acb2fc2 100644
--- a/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java
+++ b/source/java/org/alfresco/web/ui/common/renderer/ActionLinkRenderer.java
@@ -17,7 +17,9 @@
package org.alfresco.web.ui.common.renderer;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.io.Writer;
+import java.net.URLEncoder;
import java.util.Map;
import javax.faces.component.UIComponent;
@@ -133,13 +135,44 @@ public class ActionLinkRenderer extends BaseRenderer
else
{
String href = link.getHref();
- if (href.startsWith("http") == false && href.startsWith("file") == false)
+
+ // prefix the web context path if required
+ linkBuf.append(" actionParams = getParameterComponents(link);
+ if (actionParams != null)
+ {
+ boolean first = (href.indexOf('?') == -1);
+ for (String name : actionParams.keySet())
+ {
+ String paramValue = actionParams.get(name);
+ if (first)
+ {
+ linkBuf.append('?');
+ first = false;
+ }
+ else
+ {
+ linkBuf.append('&');
+ }
+ try
+ {
+ linkBuf.append(name).append("=").append(URLEncoder.encode(paramValue, "UTF-8"));
+ }
+ catch (UnsupportedEncodingException err)
+ {
+ // if this happens we have bigger problems than a missing URL parameter...!
+ }
+ }
+ }
+
+ linkBuf.append('"');
// output href 'target' attribute if supplied
if (link.getTarget() != null)
diff --git a/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java
index 4f85d1404f..6181461f69 100644
--- a/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java
+++ b/source/java/org/alfresco/web/ui/common/renderer/DatePickerRenderer.java
@@ -36,6 +36,9 @@ import javax.faces.convert.ConverterException;
import javax.faces.model.SelectItem;
import org.alfresco.web.app.Application;
+import org.alfresco.web.ui.common.Utils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
* @author kevinr
@@ -51,6 +54,12 @@ public class DatePickerRenderer extends BaseRenderer
private static final String FIELD_DAY = "_day";
private static final String FIELD_HOUR = "_hour";
private static final String FIELD_MINUTE = "_minute";
+ private static final String FIELD_CMD = "_cmd";
+ private static final int CMD_SET = 1;
+ private static final int CMD_RESET = 2;
+ private static final int CMD_TODAY = 3;
+
+ private static final Log logger = LogFactory.getLog(DatePickerRenderer.class);
/**
* @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
@@ -66,26 +75,68 @@ public class DatePickerRenderer extends BaseRenderer
// TODO: should check for disabled/readonly here - no need to decode
String clientId = component.getClientId(context);
Map params = context.getExternalContext().getRequestParameterMap();
- String year = (String)params.get(clientId + FIELD_YEAR);
- if (year != null)
+
+ // see if a command was invoked
+ String cmd = (String)params.get(clientId + FIELD_CMD);
+ if (cmd != null && cmd.length() > 0)
{
- // found data for our component
- String month = (String)params.get(clientId + FIELD_MONTH);
- String day = (String)params.get(clientId + FIELD_DAY);
- String hour = (String)params.get(clientId + FIELD_HOUR);
- String minute = (String)params.get(clientId + FIELD_MINUTE);
+ int action = Integer.parseInt(cmd);
- // we encode the values needed for the component as we see fit
- int[] parts = new int[5];
- parts[0] = Integer.parseInt(year);
- parts[1] = Integer.parseInt(month);
- parts[2] = Integer.parseInt(day);
- parts[3] = Integer.parseInt(hour);
- parts[4] = Integer.parseInt(minute);
-
- // save the data in an object for our component as the "EditableValueHolder"
- // all UI Input Components support this interface for the submitted value
- ((EditableValueHolder)component).setSubmittedValue(parts);
+ switch (action)
+ {
+ case CMD_RESET:
+ {
+ // set the submitted value to be null
+ ((EditableValueHolder)component).setSubmittedValue(null);
+
+ // set the component value to be null too
+ ((EditableValueHolder)component).setValue(null);
+
+ break;
+ }
+
+ default:
+ {
+ // the user is either trying to set the date for the first
+ // time or set it back to today's date, create the parts array
+ // to represent this and set as the submitted value
+ int[] parts = new int[5];
+
+ Calendar date = Calendar.getInstance();
+ parts[0] = date.get(Calendar.YEAR);
+ parts[1] = date.get(Calendar.MONTH);
+ parts[2] = date.get(Calendar.DAY_OF_MONTH);
+ parts[3] = date.get(Calendar.HOUR_OF_DAY);
+ parts[4] = date.get(Calendar.MINUTE);
+
+ ((EditableValueHolder)component).setSubmittedValue(parts);
+ }
+ }
+ }
+ else
+ {
+ // a command was not invoked so decode the date the user set (if present)
+ String year = (String)params.get(clientId + FIELD_YEAR);
+ if (year != null)
+ {
+ // found data for our component
+ String month = (String)params.get(clientId + FIELD_MONTH);
+ String day = (String)params.get(clientId + FIELD_DAY);
+ String hour = (String)params.get(clientId + FIELD_HOUR);
+ String minute = (String)params.get(clientId + FIELD_MINUTE);
+
+ // we encode the values needed for the component as we see fit
+ int[] parts = new int[5];
+ parts[0] = Integer.parseInt(year);
+ parts[1] = Integer.parseInt(month);
+ parts[2] = Integer.parseInt(day);
+ parts[3] = Integer.parseInt(hour);
+ parts[4] = Integer.parseInt(minute);
+
+ // save the data in an object for our component as the "EditableValueHolder"
+ // all UI Input Components support this interface for the submitted value
+ ((EditableValueHolder)component).setSubmittedValue(parts);
+ }
}
}
catch (NumberFormatException nfe)
@@ -124,6 +175,9 @@ public class DatePickerRenderer extends BaseRenderer
if (component.isRendered() == true)
{
Date date = null;
+ String clientId = component.getClientId(context);
+ ResponseWriter out = context.getResponseWriter();
+ String cmdFieldName = clientId + FIELD_CMD;
// this is part of the spec:
// first you attempt to build the date from the submitted value
@@ -136,57 +190,119 @@ public class DatePickerRenderer extends BaseRenderer
{
// second if no submitted value is found, default to the current value
Object value = ((ValueHolder)component).getValue();
- // finally check for null value and create default if needed
- date = value instanceof Date ? (Date)value : new Date();
+ if (value instanceof Date)
+ {
+ date = (Date)value;
+ }
}
- // get the attributes from the component we need for rendering
- int nStartYear;
- Integer startYear = (Integer)component.getAttributes().get("startYear");
- if (startYear != null)
+ // create a flag to show if the component is disabled
+ Boolean disabled = (Boolean)component.getAttributes().get("disabled");
+ if (disabled == null)
{
- nStartYear = startYear.intValue();
+ disabled = Boolean.FALSE;
+ }
+
+ if (date != null)
+ {
+ // get the attributes from the component we need for rendering
+ int nStartYear;
+ Integer startYear = (Integer)component.getAttributes().get("startYear");
+ if (startYear != null)
+ {
+ nStartYear = startYear.intValue();
+ }
+ else
+ {
+ nStartYear = new Date().getYear() + 1900 + 2; // for "effectivity date" searches
+ }
+
+ int nYearCount = 25;
+ Integer yearCount = (Integer)component.getAttributes().get("yearCount");
+ if (yearCount != null)
+ {
+ nYearCount = yearCount.intValue();
+ }
+
+ // now we render the output for our component
+ // we create 3 drop-down menus for day, month and year and
+ // two text fields for the hour and minute
+
+ // note that we build a client id for our form elements that we are then
+ // able to decode() as above.
+ Calendar calendar = new GregorianCalendar();
+ calendar.setTime(date);
+ renderMenu(out, component, getDays(), calendar.get(Calendar.DAY_OF_MONTH), clientId + FIELD_DAY);
+ renderMenu(out, component, getMonths(), calendar.get(Calendar.MONTH), clientId + FIELD_MONTH);
+ renderMenu(out, component, getYears(nStartYear, nYearCount), calendar.get(Calendar.YEAR), clientId + FIELD_YEAR);
+
+ // make sure we have a flag to determine whether to show the time
+ Boolean showTime = (Boolean)component.getAttributes().get("showTime");
+ if (showTime == null)
+ {
+ showTime = Boolean.FALSE;
+ }
+
+ out.write(" ");
+ renderTimeElement(out, component, calendar.get(Calendar.HOUR_OF_DAY), clientId + FIELD_HOUR, showTime.booleanValue());
+ if (showTime.booleanValue())
+ {
+ out.write(" : ");
+ }
+ renderTimeElement(out, component, calendar.get(Calendar.MINUTE), clientId + FIELD_MINUTE, showTime.booleanValue());
+ out.write(" ");
+
+ // render 2 links (if the component is not disabled) to allow the user to reset the
+ // date back to null or to select today's date
+ if (disabled.booleanValue() == false)
+ {
+ out.write(" ");
+ }
}
else
{
- nStartYear = new Date().getYear() + 1900 + 2; // for "effectivity date" searches
+ // Render a link indicating there isn't a date set (unless the property is disabled)
+ out.write("");
}
- int nYearCount = 25;
- Integer yearCount = (Integer)component.getAttributes().get("yearCount");
- if (yearCount != null)
+ // also output a hidden field containing the current value of the date, this will
+ // allow JavaScript to determine if a value is set for validation purposes.
+ out.write(" ");
}
}
diff --git a/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java b/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java
index dc7a8b1ebe..e3212a7212 100644
--- a/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java
+++ b/source/java/org/alfresco/web/ui/common/renderer/ImagePickerRadioRenderer.java
@@ -50,6 +50,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer
private int columns;
private int position;
private boolean open;
+ private boolean imageSelected = false;
// ------------------------------------------------------------------------------
// Renderer implemenation
@@ -89,6 +90,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer
this.columns = 1;
this.position = 0;
this.open = false;
+ this.imageSelected = false;
ResponseWriter out = context.getResponseWriter();
@@ -210,6 +212,14 @@ public class ImagePickerRadioRenderer extends BaseRenderer
ResponseWriter out = context.getResponseWriter();
out.write("");
+
+ // if we didn't select any image, default to the first one
+ if (this.imageSelected == false)
+ {
+ out.write("\n\n");
+ }
}
/**
@@ -275,6 +285,7 @@ public class ImagePickerRadioRenderer extends BaseRenderer
if (itemValue != null && itemValue.equals(currentValue))
{
out.write(" checked='true'");
+ this.imageSelected = true;
}
if (tooltip != null)
diff --git a/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java b/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java
index 24c3151c86..f88c181ae9 100644
--- a/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java
+++ b/source/java/org/alfresco/web/ui/common/tag/GenericPickerTag.java
@@ -20,7 +20,6 @@ import javax.faces.FacesException;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.el.MethodBinding;
-import javax.faces.el.ValueBinding;
import org.alfresco.web.ui.common.component.UIGenericPicker;
@@ -57,6 +56,7 @@ public class GenericPickerTag extends BaseComponentTag
setBooleanProperty(component, "showContains", this.showContains);
setBooleanProperty(component, "showAddButton", this.showAddButton);
setBooleanProperty(component, "filterRefresh", this.filterRefresh);
+ setBooleanProperty(component, "multiSelect", this.multiSelect);
setStringProperty(component, "addButtonLabel", this.addButtonLabel);
setActionProperty((UICommand)component, this.action);
setActionListenerProperty((UICommand)component, this.actionListener);
@@ -94,6 +94,7 @@ public class GenericPickerTag extends BaseComponentTag
this.queryCallback = null;
this.filters = null;
this.filterRefresh = null;
+ this.multiSelect = null;
}
/**
@@ -205,8 +206,20 @@ public class GenericPickerTag extends BaseComponentTag
{
this.filterRefresh = filterRefresh;
}
+
+ /**
+ * Set the multiSelect
+ *
+ * @param mutliSelect the multiSelect
+ */
+ public void setMultiSelect(String multiSelect)
+ {
+ this.multiSelect = multiSelect;
+ }
-
+ /** the multiSelect */
+ private String multiSelect;
+
/** the filterRefresh */
private String filterRefresh;
diff --git a/source/java/org/alfresco/web/ui/repo/RepoConstants.java b/source/java/org/alfresco/web/ui/repo/RepoConstants.java
index c2f2597d0d..079c26176d 100644
--- a/source/java/org/alfresco/web/ui/repo/RepoConstants.java
+++ b/source/java/org/alfresco/web/ui/repo/RepoConstants.java
@@ -26,6 +26,7 @@ public final class RepoConstants
public static final String ALFRESCO_FACES_ASSOCIATION = "org.alfresco.faces.Association";
public static final String ALFRESCO_FACES_CHILD_ASSOCIATION = "org.alfresco.faces.ChildAssociation";
public static final String ALFRESCO_FACES_PROPERTY = "org.alfresco.faces.Property";
+ public static final String ALFRESCO_FACES_SEPARATOR = "org.alfresco.faces.Separator";
public static final String ALFRESCO_FACES_SPACE_SELECTOR = "org.alfresco.faces.SpaceSelector";
public static final String ALFRESCO_FACES_ASSOC_EDITOR = "org.alfresco.faces.AssociationEditor";
public static final String ALFRESCO_FACES_CHILD_ASSOC_EDITOR = "org.alfresco.faces.ChildAssociationEditor";
@@ -43,12 +44,15 @@ public final class RepoConstants
public static final String GENERATOR_LABEL = "LabelGenerator";
public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator";
+ public static final String GENERATOR_TEXT_AREA = "TextAreaGenerator";
public static final String GENERATOR_CHECKBOX = "CheckboxGenerator";
public static final String GENERATOR_DATE_PICKER = "DatePickerGenerator";
public static final String GENERATOR_DATETIME_PICKER = "DateTimePickerGenerator";
public static final String GENERATOR_CATEGORY_SELECTOR = "CategorySelectorGenerator";
public static final String GENERATOR_ASSOCIATION = "AssociationGenerator";
public static final String GENERATOR_CHILD_ASSOCIATION = "ChildAssociationGenerator";
+ public static final String GENERATOR_SEPARATOR = "SeparatorGenerator";
+ public static final String GENERATOR_HEADER_SEPARATOR = "HeaderSeparatorGenerator";
/**
* Private constructor
diff --git a/source/java/org/alfresco/web/ui/repo/component/UIActions.java b/source/java/org/alfresco/web/ui/repo/component/UIActions.java
index bdbbce52c8..dcebb9290c 100644
--- a/source/java/org/alfresco/web/ui/repo/component/UIActions.java
+++ b/source/java/org/alfresco/web/ui/repo/component/UIActions.java
@@ -17,6 +17,7 @@
package org.alfresco.web.ui.repo.component;
import java.io.IOException;
+import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -30,6 +31,7 @@ import javax.faces.el.ValueBinding;
import org.alfresco.config.Config;
import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.config.ActionsConfigElement;
@@ -369,6 +371,7 @@ public class UIActions extends SelfRenderingComponent
control.setOnclick(actionDef.Onclick);
}
}
+
if (actionDef.Href != null)
{
if (UIComponentTagUtils.isValueReference(actionDef.Href))
@@ -380,6 +383,29 @@ public class UIActions extends SelfRenderingComponent
control.setHref(actionDef.Href);
}
}
+ else if (actionDef.Script != null && actionDef.Script.length() != 0)
+ {
+ // found a script reference - may be a Path or a NodeRef
+ StringBuilder scriptHref = new StringBuilder(100);
+ scriptHref.append("/command/script/execute");
+ if (actionDef.Script.charAt(0) == '/')
+ {
+ // found a Path - encode it as a URL argument
+ scriptHref.append("?scriptPath=");
+ scriptHref.append(Utils.replace(URLEncoder.encode(actionDef.Script, "UTF-8"), "+", "%20"));
+ }
+ else
+ {
+ // found a NodeRef string, encode as URL elements
+ NodeRef ref = new NodeRef(actionDef.Script);
+ scriptHref.append('/').append(ref.getStoreRef().getProtocol())
+ .append('/').append(ref.getStoreRef().getIdentifier())
+ .append('/').append(ref.getId());
+ }
+ // set the full script execution URL as the href for the control
+ control.setHref(scriptHref.toString());
+ }
+
control.setTarget(actionDef.Target);
control.setImage(actionDef.Image);
diff --git a/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java b/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java
new file mode 100644
index 0000000000..e9f19c9927
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/UIDialogButtons.java
@@ -0,0 +1,292 @@
+package org.alfresco.web.ui.repo.component;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.faces.component.UICommand;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
+import javax.faces.component.html.HtmlCommandButton;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.el.MethodBinding;
+import javax.faces.el.ValueBinding;
+
+import org.alfresco.web.app.Application;
+import org.alfresco.web.app.servlet.FacesHelper;
+import org.alfresco.web.config.DialogsConfigElement.DialogButtonConfig;
+import org.alfresco.web.ui.common.ComponentConstants;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.component.SelfRenderingComponent;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Component that displays the buttons for a dialog.
+ *
+ * The standard OK
and Cancel
buttons
+ * are always generated. Any additional buttons, either configured
+ * or generated dynamically by the dialog, are generated in between
+ * the standard buttons.
+ *
+ * @author gavinc
+ */
+public class UIDialogButtons extends SelfRenderingComponent
+{
+ protected static final String BINDING_EXPRESSION_START = "#{";
+
+ private static final Log logger = LogFactory.getLog(UIDialogButtons.class);
+
+ @Override
+ public String getFamily()
+ {
+ return "org.alfresco.faces.DialogButtons";
+ }
+
+ @Override
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ if (this.getChildCount() == 0)
+ {
+ // generate all the required buttons the first time
+ generateButtons(context);
+ }
+
+ ResponseWriter out = context.getResponseWriter();
+ out.write("
");
+ }
+
+ @Override
+ public void encodeChildren(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ ResponseWriter out = context.getResponseWriter();
+
+ // render the buttons
+ for (Iterator i = getChildren().iterator(); i.hasNext(); /**/)
+ {
+ out.write("");
+
+ UIComponent child = (UIComponent)i.next();
+ Utils.encodeRecursive(context, child);
+
+ out.write(" ");
+ }
+ }
+
+ @Override
+ public void encodeEnd(FacesContext context) throws IOException
+ {
+ if (!isRendered()) return;
+
+ ResponseWriter out = context.getResponseWriter();
+ out.write("
");
+ }
+
+ @Override
+ public boolean getRendersChildren()
+ {
+ return true;
+ }
+
+ /**
+ * Generates the buttons for the dialog currently being shown.
+ *
+ * @param context Faces context
+ */
+ @SuppressWarnings("unchecked")
+ protected void generateButtons(FacesContext context)
+ {
+ // generate the OK button, if necessary
+ if (Application.getDialogManager().isOKButtonVisible())
+ {
+ UICommand okButton = (UICommand)context.getApplication().
+ createComponent(HtmlCommandButton.COMPONENT_TYPE);
+ okButton.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON);
+ FacesHelper.setupComponentId(context, okButton, "finish-button");
+
+ // create the binding for the finish button label
+ ValueBinding valueBinding = context.getApplication().createValueBinding(
+ "#{DialogManager.finishButtonLabel}");
+ okButton.setValueBinding("value", valueBinding);
+
+ // create the action binding
+ MethodBinding methodBinding = context.getApplication().createMethodBinding(
+ "#{DialogManager.finish}", null);
+ okButton.setAction(methodBinding);
+
+ // create the binding for whether the button is disabled
+ valueBinding = context.getApplication().createValueBinding(
+ "#{DialogManager.finishButtonDisabled}");
+ okButton.setValueBinding("disabled", valueBinding);
+
+ // setup CSS class for button
+ String styleClass = (String)this.getAttributes().get("styleClass");
+ if (styleClass != null)
+ {
+ okButton.getAttributes().put("styleClass", styleClass);
+ }
+
+ // add the OK button
+ this.getChildren().add(okButton);
+ }
+
+ // generate the additional buttons
+ generateAdditionalButtons(context);
+
+ // generate the OK button
+ UICommand cancelButton = (UICommand)context.getApplication().
+ createComponent(HtmlCommandButton.COMPONENT_TYPE);
+ cancelButton.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON);
+ FacesHelper.setupComponentId(context, cancelButton, "cancel-button");
+
+ // create the binding for the cancel button label
+ ValueBinding valueBinding = context.getApplication().createValueBinding(
+ "#{DialogManager.cancelButtonLabel}");
+ cancelButton.setValueBinding("value", valueBinding);
+
+ // create the action binding
+ MethodBinding methodBinding = context.getApplication().createMethodBinding(
+ "#{DialogManager.cancel}", null);
+ cancelButton.setAction(methodBinding);
+
+ // setup CSS class for button
+ String styleClass = (String)this.getAttributes().get("styleClass");
+ if (styleClass != null)
+ {
+ cancelButton.getAttributes().put("styleClass", styleClass);
+ }
+
+ // set the immediate flag to true
+ cancelButton.getAttributes().put("immediate", Boolean.TRUE);
+
+ // add the Cancel button
+ this.getChildren().add(cancelButton);
+ }
+
+ /**
+ * If there are any additional buttons to add as defined by the dialog
+ * configuration and the dialog at runtime they are generated in this
+ * method.
+ *
+ * @param context Faces context
+ */
+ @SuppressWarnings("unchecked")
+ protected void generateAdditionalButtons(FacesContext context)
+ {
+ // get potential list of additional buttons
+ List buttons = Application.getDialogManager().getAdditionalButtons();
+
+ if (buttons != null && buttons.size() > 0)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Adding " + buttons.size() + " additional buttons: " + buttons);
+
+ for (DialogButtonConfig buttonCfg : buttons)
+ {
+ UICommand button = (UICommand)context.getApplication().
+ createComponent(HtmlCommandButton.COMPONENT_TYPE);
+ button.setRendererType(ComponentConstants.JAVAX_FACES_BUTTON);
+ FacesHelper.setupComponentId(context, button, buttonCfg.getId());
+
+ // setup the value of the button (the label)
+ String label = buttonCfg.getLabel();
+ if (label != null)
+ {
+ // see if the label represents a value binding
+ if (label.startsWith(BINDING_EXPRESSION_START))
+ {
+ ValueBinding binding = context.getApplication().createValueBinding(label);
+ button.setValueBinding("value", binding);
+ }
+ else
+ {
+ button.setValue(label);
+ }
+ }
+ else
+ {
+ // NOTE: the config checks that a label or a label id
+ // is present so we can assume there is an id
+ // if there isn't a label
+ String labelId = buttonCfg.getLabelId();
+ label = Application.getMessage(context, labelId);
+ button.setValue(label);
+ }
+
+ // setup the action binding, the config checks that an action
+ // is present so no need to check for NullPointer. It also checks
+ // it represents a method binding expression.
+ String action = buttonCfg.getAction();
+ MethodBinding methodBinding = context.getApplication().
+ createMethodBinding(action, null);
+ button.setAction(methodBinding);
+
+ // setup the disabled attribute, check for null and
+ // binding expressions
+ String disabled = buttonCfg.getDisabled();
+ if (disabled != null && disabled.length() > 0)
+ {
+ if (disabled.startsWith(BINDING_EXPRESSION_START))
+ {
+ ValueBinding binding = context.getApplication().
+ createValueBinding(disabled);
+ button.setValueBinding("disabled", binding);
+ }
+ else
+ {
+ button.getAttributes().put("disabled",
+ Boolean.parseBoolean(disabled));
+ }
+ }
+
+ // setup CSS class for the button
+ String styleClass = (String)this.getAttributes().get("styleClass");
+ if (styleClass != null)
+ {
+ button.getAttributes().put("styleClass", styleClass);
+ }
+
+ // setup the onclick handler for the button
+ String onclick = buttonCfg.getOnclick();
+ if (onclick != null && onclick.length() > 0)
+ {
+ button.getAttributes().put("onclick", onclick);
+ }
+
+ // add the button
+ this.getChildren().add(button);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Added button with id of: " + button.getId());
+ }
+
+ // add a spacing row to separate the additional buttons from the Cancel button
+ addSpacingRow(context);
+ }
+ }
+
+ /**
+ * Creates an output text component to represent a spacing row.
+ *
+ * @param context Faces context
+ */
+ @SuppressWarnings("unchecked")
+ protected void addSpacingRow(FacesContext context)
+ {
+ UIOutput spacingRow = (UIOutput)context.getApplication().createComponent(
+ ComponentConstants.JAVAX_FACES_OUTPUT);
+ spacingRow.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
+ FacesHelper.setupComponentId(context, spacingRow, null);
+ spacingRow.setValue("
");
+ spacingRow.getAttributes().put("escape", Boolean.FALSE);
+ this.getChildren().add(spacingRow);
+ }
+}
+
+
+
diff --git a/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java b/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java
index b45916ab67..fc085b9029 100644
--- a/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java
+++ b/source/java/org/alfresco/web/ui/repo/component/UISearchCustomProperties.java
@@ -78,6 +78,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
/**
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
*/
+ @SuppressWarnings("unchecked")
public void encodeBegin(FacesContext context) throws IOException
{
if (isRendered() == false)
@@ -133,6 +134,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
*
* @param context FacesContext
*/
+ @SuppressWarnings("unchecked")
private void createComponentsFromConfig(FacesContext context)
{
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
@@ -236,6 +238,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
*
* @return UIComponent
*/
+ @SuppressWarnings("unchecked")
private UIComponent generateControl(FacesContext context, PropertyDefinition propDef, String displayLabel, String beanBinding)
{
UIComponent control = null;
@@ -265,6 +268,22 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
{
Boolean showTime = Boolean.valueOf(typeName.equals(DataTypeDefinition.DATETIME));
+ // create value bindings for the start year and year count attributes
+ ValueBinding startYearBind = null;
+ ValueBinding yearCountBind = null;
+
+ if (showTime)
+ {
+ startYearBind = facesApp.createValueBinding("#{DateTimePickerGenerator.startYear}");
+ yearCountBind = facesApp.createValueBinding("#{DateTimePickerGenerator.yearCount}");
+ }
+ else
+ {
+ startYearBind = facesApp.createValueBinding("#{DatePickerGenerator.startYear}");
+ yearCountBind = facesApp.createValueBinding("#{DatePickerGenerator.yearCount}");
+ }
+
+
// Need to output component for From and To date selectors and labels
// also neeed checkbox for enable/disable state - requires an outer wrapper component
control = (UIPanel)facesApp.createComponent(ComponentConstants.JAVAX_FACES_PANEL);
@@ -298,7 +317,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
UIInput inputFromDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
inputFromDate.setId(context.getViewRoot().createUniqueId());
inputFromDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
- inputFromDate.getAttributes().put("yearCount", new Integer(30));
+ inputFromDate.setValueBinding("startYear", startYearBind);
+ inputFromDate.setValueBinding("yearCount", yearCountBind);
inputFromDate.getAttributes().put("showTime", showTime);
ValueBinding vbFromDate = facesApp.createValueBinding(
"#{" + beanBinding + "[\"" + PREFIX_DATE_FROM + propDef.getName().toString() + "\"]}");
@@ -316,7 +336,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
UIInput inputToDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
inputToDate.setId(context.getViewRoot().createUniqueId());
inputToDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
- inputToDate.getAttributes().put("yearCount", new Integer(30));
+ inputToDate.setValueBinding("startYear", startYearBind);
+ inputToDate.setValueBinding("yearCount", yearCountBind);
inputToDate.getAttributes().put("showTime", showTime);
ValueBinding vbToDate = facesApp.createValueBinding(
"#{" + beanBinding + "[\"" + PREFIX_DATE_TO + propDef.getName().toString() + "\"]}");
@@ -333,8 +354,8 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
// any other type is represented as an input text field
control = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
- control.getAttributes().put("size", "28");
- control.getAttributes().put("maxlength", "1024");
+ control.setValueBinding("size", facesApp.createValueBinding("#{TextFieldGenerator.size}"));
+ control.setValueBinding("maxlength", facesApp.createValueBinding("#{TextFieldGenerator.maxLength}"));
control.setValueBinding(VALUE, vb);
}
diff --git a/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java b/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java
new file mode 100644
index 0000000000..fd99017deb
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/UIUserGroupPicker.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.ui.repo.component;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UICommand;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.event.ActionEvent;
+
+import org.alfresco.web.app.Application;
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.WebResources;
+
+/**
+ * Seld rendering component tied to the EmailSpaceUsersDialog bean. Renders a hierarchy of
+ * user/group authorities. Each authority can be (de)selected and groups can be expanded/collapsed
+ * to display and select from the child authorities in the group. Nested groups are supported.
+ *
+ * @author Kevin Roast
+ */
+public class UIUserGroupPicker extends UICommand
+{
+ /** action ids */
+ public final static int ACTION_NONE = -1;
+ public final static int ACTION_EXPANDCOLLAPSE = 0;
+ public final static int ACTION_SELECT = 1;
+
+ private static String SELECTED_AUTHORITY = "_check";
+
+
+ // ------------------------------------------------------------------------------
+ // Component implementation
+
+ /**
+ * Default constructor
+ */
+ public UIUserGroupPicker()
+ {
+ setRendererType(null);
+ }
+
+ /**
+ * @see javax.faces.component.UIComponent#getFamily()
+ */
+ public String getFamily()
+ {
+ return "org.alfresco.faces.UserGroupPicker";
+ }
+
+ /**
+ * @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
+ */
+ public void decode(FacesContext context)
+ {
+ Map requestMap = context.getExternalContext().getRequestParameterMap();
+ Map valuesMap = context.getExternalContext().getRequestParameterValuesMap();
+ String fieldId = getHiddenFieldName(context);
+ String value = (String)requestMap.get(fieldId);
+
+ if (value != null && value.length() != 0)
+ {
+ // decode the values - we are expecting an action identifier and an authority name
+ int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
+ int action = Integer.parseInt(value.substring(0, sepIndex));
+ String authority = value.substring(sepIndex + 1);
+
+ // queue an event
+ PickerEvent event = new PickerEvent(this, action, authority);
+ queueEvent(event);
+ }
+ }
+
+ /**
+ * @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
+ */
+ public void encodeBegin(FacesContext context) throws IOException
+ {
+ if (isRendered() == false)
+ {
+ return;
+ }
+
+ ResponseWriter out = context.getResponseWriter();
+
+ ResourceBundle bundle = Application.getBundle(context);
+
+ String clientId = getClientId(context);
+
+ // start outer table
+ out.write("");
+
+ // get the data that represents the users/groups to display
+ List userGroups = (List)getValue();
+ if (userGroups != null)
+ {
+ for (Map authority : userGroups)
+ {
+ String authorityId = (String)authority.get("id");
+
+ out.write("");
+
+ // walk parent hierarchy to calculate width of this cell
+ int width = 16;
+ Map parent = (Map)authority.get("parent");
+ while (parent != null)
+ {
+ width += 16;
+ parent = (Map)parent.get("parent");
+ }
+ out.write("");
+
+ // output expanded/collapsed icon if authority is a group
+ boolean expanded = false;
+ boolean isGroup = (Boolean)authority.get("isGroup");
+ if (isGroup)
+ {
+ // either output the expanded or collapsed selectable widget
+ expanded = (Boolean)authority.get("expanded");
+ String image = expanded ? WebResources.IMAGE_EXPANDED : WebResources.IMAGE_COLLAPSED;
+ out.write(Utils.buildImageTag(context, image, 11, 11, "",
+ generateFormSubmit(context, ACTION_EXPANDCOLLAPSE, authorityId)));
+ }
+ out.write(" ");
+
+ // output selected checkbox if not expanded and not a duplicate
+ boolean duplicate = (Boolean)authority.get("duplicate");
+ if (duplicate == false && (isGroup == false || expanded == false))
+ {
+ boolean selected = (Boolean)authority.get("selected");
+ out.write(" ');
+ }
+ out.write(" ");
+
+ // output icon
+ out.write(Utils.buildImageTag(context, (String)authority.get("icon"), 16, 16, ""));
+ out.write(" ");
+
+ // output textual information
+ if (duplicate)
+ {
+ out.write("");
+ }
+ out.write((String)authority.get("fullName"));
+ out.write(" (");
+ out.write((String)authority.get("roles"));
+ out.write(")");
+ if (duplicate)
+ {
+ out.write(" ");
+ }
+ out.write(" ");
+
+ out.write("
");
+ }
+ }
+
+ out.write("
");
+ }
+
+
+ // ------------------------------------------------------------------------------
+ // Private helpers
+
+ /**
+ * We use a hidden field per picker instance on the page.
+ *
+ * @return hidden field name
+ */
+ private String getHiddenFieldName(FacesContext context)
+ {
+ return getClientId(context);
+ }
+
+ /**
+ * Generate FORM submit JavaScript for the specified action
+ *
+ * @param context FacesContext
+ * @param action Action index
+ * @param authority Authority Id of the action source
+ *
+ * @return FORM submit JavaScript
+ */
+ private String generateFormSubmit(FacesContext context, int action, String authority)
+ {
+ return Utils.generateFormSubmit(context, this, getHiddenFieldName(context),
+ Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + authority);
+ }
+
+
+ // ------------------------------------------------------------------------------
+ // Inner classes
+
+ /**
+ * Class representing the an action relevant to the User Group picker component.
+ */
+ public static class PickerEvent extends ActionEvent
+ {
+ public PickerEvent(UIComponent component, int action, String authority)
+ {
+ super(component);
+ Action = action;
+ Authority = authority;
+ }
+
+ public String Authority;
+ public int Action;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java
index 1bf6901a1f..b79bceb239 100644
--- a/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java
+++ b/source/java/org/alfresco/web/ui/repo/component/property/BaseAssociationEditor.java
@@ -32,6 +32,7 @@ import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.FacesEvent;
+import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -698,9 +699,17 @@ public abstract class BaseAssociationEditor extends UIInput
}
out.write("'>");
- out.write(Repository.getDisplayPath(nodeService.getPath(targetRef)));
- out.write("/");
- out.write(Repository.getNameForNode(nodeService, targetRef));
+ if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetRef)))
+ {
+ out.write((String)nodeService.getProperty(targetRef, ContentModel.PROP_USERNAME));
+ }
+ else
+ {
+ out.write(Repository.getDisplayPath(nodeService.getPath(targetRef)));
+ out.write("/");
+ out.write(Repository.getNameForNode(nodeService, targetRef));
+ }
+
out.write("");
- out.write(Repository.getDisplayPath(nodeService.getPath(item)));
- out.write("/");
- out.write(Repository.getNameForNode(nodeService, item));
+ // if the node represents a person, show the username instead of the name
+ if (ContentModel.TYPE_PERSON.equals(nodeService.getType(item)))
+ {
+ out.write((String)nodeService.getProperty(item, ContentModel.PROP_USERNAME));
+ }
+ else
+ {
+ out.write(Repository.getDisplayPath(nodeService.getPath(item)));
+ out.write("/");
+ out.write(Repository.getNameForNode(nodeService, item));
+ }
out.write("");
}
}
@@ -852,17 +869,31 @@ public abstract class BaseAssociationEditor extends UIInput
if (assocDef != null)
{
// find and show all the available options for the current association
+ String type = assocDef.getTargetClass().getName().toString();
StringBuilder query = new StringBuilder("+TYPE:\"");
- query.append(assocDef.getTargetClass().getName().toString());
+ query.append(type);
query.append("\"");
if (contains != null && contains.length() > 0)
{
String safeContains = Utils.remove(contains.trim(), "\"");
- String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
-
query.append(" AND +@");
- query.append(nameAttr);
+
+ // if the association's target is the person type search on the
+ // username instead of the name property
+ if (type.equals(ContentModel.TYPE_PERSON.toString()))
+ {
+ String userName = Repository.escapeQName(QName.createQName(
+ NamespaceService.CONTENT_MODEL_1_0_URI, "userName"));
+ query.append(userName);
+ }
+ else
+ {
+ String nameAttr = Repository.escapeQName(QName.createQName(
+ NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
+ query.append(nameAttr);
+ }
+
query.append(":*" + safeContains + "*");
}
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java
index 3f9a10a2bc..dbd65eaf0d 100644
--- a/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java
+++ b/source/java/org/alfresco/web/ui/repo/component/property/UIAssociationEditor.java
@@ -26,6 +26,7 @@ import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
+import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -152,9 +153,18 @@ public class UIAssociationEditor extends BaseAssociationEditor
{
out.write(" ");
AssociationRef assoc = (AssociationRef)iter.next();
- out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getTargetRef())));
- out.write("/");
- out.write(Repository.getNameForNode(nodeService, assoc.getTargetRef()));
+ NodeRef targetNode = assoc.getTargetRef();
+ // if the node represents a person, show the username instead of the name
+ if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetNode)))
+ {
+ out.write((String)nodeService.getProperty(targetNode, ContentModel.PROP_USERNAME));
+ }
+ else
+ {
+ out.write(Repository.getDisplayPath(nodeService.getPath(targetNode)));
+ out.write("/");
+ out.write(Repository.getNameForNode(nodeService, targetNode));
+ }
out.write(" ");
}
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java b/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java
index 3c0d510a41..9d693ffc36 100644
--- a/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java
+++ b/source/java/org/alfresco/web/ui/repo/component/property/UIChildAssociationEditor.java
@@ -26,6 +26,7 @@ import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
+import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -152,9 +153,19 @@ public class UIChildAssociationEditor extends BaseAssociationEditor
{
out.write("");
ChildAssociationRef assoc = (ChildAssociationRef)iter.next();
- out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getChildRef())));
- out.write("/");
- out.write(Repository.getNameForNode(nodeService, assoc.getChildRef()));
+ NodeRef targetNode = assoc.getChildRef();
+ // if the node represents a person, show the username instead of the name
+ if (ContentModel.TYPE_PERSON.equals(nodeService.getType(targetNode)))
+ {
+ out.write((String)nodeService.getProperty(targetNode, ContentModel.PROP_USERNAME));
+ }
+ else
+ {
+ out.write(Repository.getDisplayPath(nodeService.getPath(targetNode)));
+ out.write("/");
+ out.write(Repository.getNameForNode(nodeService, targetNode));
+ }
+
out.write(" ");
}
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java
index 9dea96b0af..d6b4b7c09c 100644
--- a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java
+++ b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java
@@ -178,7 +178,8 @@ public class UIProperty extends PropertySheetItem
{
// if we are trying to edit a NodeRef or Path property type set it to read-only as
// these are internal properties that shouldn't be edited.
- if (typeName.equals(DataTypeDefinition.NODE_REF) || typeName.equals(DataTypeDefinition.PATH))
+ if (typeName.equals(DataTypeDefinition.NODE_REF) || typeName.equals(DataTypeDefinition.PATH) ||
+ typeName.equals(DataTypeDefinition.CONTENT))
{
logger.warn("Setting property " + propDef.getName().toString() + " to read-only as it can not be edited");
control.getAttributes().put("disabled", Boolean.TRUE);
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java b/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java
index d7d4b75bef..d60b492400 100644
--- a/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java
+++ b/source/java/org/alfresco/web/ui/repo/component/property/UIPropertySheet.java
@@ -35,6 +35,7 @@ import org.alfresco.config.Config;
import org.alfresco.config.ConfigLookupContext;
import org.alfresco.config.ConfigService;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.Node;
@@ -43,6 +44,7 @@ import org.alfresco.web.config.PropertySheetConfigElement.AssociationConfig;
import org.alfresco.web.config.PropertySheetConfigElement.ChildAssociationConfig;
import org.alfresco.web.config.PropertySheetConfigElement.ItemConfig;
import org.alfresco.web.config.PropertySheetConfigElement.PropertyConfig;
+import org.alfresco.web.config.PropertySheetConfigElement.SeparatorConfig;
import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.repo.RepoConstants;
@@ -63,6 +65,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
private static String DEFAULT_VAR_NAME = "node";
private static String PROP_ID_PREFIX = "prop_";
private static String ASSOC_ID_PREFIX = "assoc_";
+ private static String SEP_ID_PREFIX = "sep_";
private List validations = new ArrayList();
private String variable;
@@ -624,8 +627,13 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
// create the property component
UIProperty propComp = (UIProperty)context.getApplication().
createComponent(RepoConstants.ALFRESCO_FACES_PROPERTY);
- FacesHelper.setupComponentId(context, propComp, PROP_ID_PREFIX + propertyName);
- propComp.setName(propertyName);
+
+ // get the property name in it's prefix form
+ QName qname = QName.createQName(propertyName);
+ String prefixPropName = qname.toPrefixString();
+
+ FacesHelper.setupComponentId(context, propComp, PROP_ID_PREFIX + prefixPropName);
+ propComp.setName(prefixPropName);
// if this property sheet is set as read only, set all properties to read only
if (isReadOnly())
@@ -640,7 +648,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
if (logger.isDebugEnabled())
logger.debug("Created property component " + propComp + "(" +
propComp.getClientId(context) +
- ") for '" + propertyName +
+ ") for '" + prefixPropName +
"' and added it to property sheet " + this);
}
@@ -652,8 +660,13 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
String assocName = (String)iter.next();
UIAssociation assocComp = (UIAssociation)context.getApplication().
createComponent(RepoConstants.ALFRESCO_FACES_ASSOCIATION);
- FacesHelper.setupComponentId(context, assocComp, ASSOC_ID_PREFIX + assocName);
- assocComp.setName(assocName);
+
+ // get the association name in it's prefix form
+ QName qname = QName.createQName(assocName);
+ String prefixAssocName = qname.toPrefixString();
+
+ FacesHelper.setupComponentId(context, assocComp, ASSOC_ID_PREFIX + prefixAssocName);
+ assocComp.setName(prefixAssocName);
// if this property sheet is set as read only, set all properties to read only
if (isReadOnly())
@@ -668,7 +681,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
if (logger.isDebugEnabled())
logger.debug("Created association component " + assocComp + "(" +
assocComp.getClientId(context) +
- ") for '" + assocName +
+ ") for '" + prefixAssocName +
"' and added it to property sheet " + this);
}
@@ -737,6 +750,12 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
propSheetItem = (PropertySheetItem)context.getApplication().
createComponent(RepoConstants.ALFRESCO_FACES_CHILD_ASSOCIATION);
}
+ else if (item instanceof SeparatorConfig)
+ {
+ id = SEP_ID_PREFIX + item.getName();
+ propSheetItem = (PropertySheetItem)context.getApplication().
+ createComponent(RepoConstants.ALFRESCO_FACES_SEPARATOR);
+ }
// now setup the common stuff across all component types
if (propSheetItem != null)
diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java b/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java
new file mode 100644
index 0000000000..5059ae26c3
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/component/property/UISeparator.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.ui.repo.component.property;
+
+import java.io.IOException;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.alfresco.web.app.servlet.FacesHelper;
+import org.alfresco.web.ui.repo.RepoConstants;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Component to represent a separator within a property sheet
+ *
+ * @author gavinc
+ */
+public class UISeparator extends PropertySheetItem
+{
+ public static final String COMPONENT_FAMILY = "org.alfresco.faces.Separator";
+
+ private static Log logger = LogFactory.getLog(UISeparator.class);
+
+ /**
+ * Default constructor
+ */
+ public UISeparator()
+ {
+ // set the default renderer
+ setRendererType("org.alfresco.faces.SeparatorRenderer");
+ }
+
+ /**
+ * @see javax.faces.component.UIComponent#getFamily()
+ */
+ public String getFamily()
+ {
+ return COMPONENT_FAMILY;
+ }
+
+ protected String getIncorrectParentMsg()
+ {
+ return "The separator component must be nested within a property sheet component";
+ }
+
+ protected void generateItem(FacesContext context, UIPropertySheet propSheet) throws IOException
+ {
+ String componentGeneratorName = this.getComponentGenerator();
+
+ if (componentGeneratorName == null)
+ {
+ componentGeneratorName = RepoConstants.GENERATOR_SEPARATOR;
+ }
+
+ UIComponent separator = FacesHelper.getComponentGenerator(context, componentGeneratorName).
+ generateAndAdd(context, propSheet, this);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Created separator " + separator + "(" +
+ separator.getClientId(context) +
+ ") for '" + this.getName() +
+ "' and added it to component " + this);
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java b/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java
index f7676bbcc7..a24798c76e 100644
--- a/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java
+++ b/source/java/org/alfresco/web/ui/repo/component/template/DefaultModelHelper.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.template.DateCompareMethod;
+import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.HasAspectMethod;
import org.alfresco.repo.template.I18NMessageMethod;
import org.alfresco.service.ServiceRegistry;
@@ -69,37 +70,11 @@ public class DefaultModelHelper
throw new IllegalArgumentException("Current User is mandatory.");
}
- // create FreeMarker default model and merge
- Map root = new HashMap(16, 1.0f);
-
- // supply the CompanyHome space as "companyhome"
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
- TemplateNode companyRootNode = new TemplateNode(companyRootRef, services, imageResolver);
- root.put("companyhome", companyRootNode);
-
- // supply the users Home Space as "userhome"
NodeRef userRootRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
- TemplateNode userRootNode = new TemplateNode(userRootRef, services, imageResolver);
- root.put("userhome", userRootNode);
- // supply the current user Node as "person"
- root.put("person", new TemplateNode(user.getPerson(), services, imageResolver));
-
- // add the template itself as "template" if it comes from content on a node
- if (template != null)
- {
- root.put("template", new TemplateNode(template, services, imageResolver));
- }
-
- // current date/time is useful to have and isn't supplied by FreeMarker by default
- root.put("date", new Date());
-
- // add custom method objects
- root.put("hasAspect", new HasAspectMethod());
- root.put("message", new I18NMessageMethod());
- root.put("dateCompare", new DateCompareMethod());
-
- return root;
+ return FreeMarkerProcessor.buildDefaultModel(
+ services, user.getPerson(), companyRootRef, userRootRef, template, imageResolver);
}
/** Template Image resolver helper */
diff --git a/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java
index 82913887a7..bdc291f124 100644
--- a/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java
+++ b/source/java/org/alfresco/web/ui/repo/renderer/property/PropertySheetItemRenderer.java
@@ -70,8 +70,6 @@ public class PropertySheetItemRenderer extends BaseRenderer
UIComponent label = children.get(0);
UIComponent control = children.get(1);
- out.write("");
-
// encode the mandatory marker component if present
if (count == 3)
{
diff --git a/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java
new file mode 100644
index 0000000000..24ab159804
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/renderer/property/SeparatorRenderer.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.ui.repo.renderer.property;
+
+import java.io.IOException;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.alfresco.web.ui.common.Utils;
+import org.alfresco.web.ui.common.renderer.BaseRenderer;
+
+/**
+ * Renderer for a Separator component
+ *
+ * @author gavinc
+ */
+public class SeparatorRenderer extends BaseRenderer
+{
+ /**
+ * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
+ */
+ public void encodeBegin(FacesContext context, UIComponent component) throws IOException
+ {
+ if (component.isRendered() == false)
+ {
+ return;
+ }
+
+ // NOTE: we close off the first generated by the property sheet's grid renderer
+ context.getResponseWriter().write(" ");
+ }
+
+ /**
+ * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
+ */
+ @SuppressWarnings("unchecked")
+ public void encodeChildren(FacesContext context, UIComponent component) throws IOException
+ {
+ if (component.isRendered() == false)
+ {
+ return;
+ }
+
+ ResponseWriter out = context.getResponseWriter();
+
+ int count = component.getChildCount();
+
+ if (count == 1)
+ {
+ // there should be 3 columns so write out a td with colspan of 3
+ // then render the child component
+ out.write("");
+ Utils.encodeRecursive(context, (UIComponent)component.getChildren().get(0));
+
+ // NOTE: we'll allow the property sheet's grid renderer close off the last
+ }
+ }
+
+ /**
+ * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
+ */
+ public void encodeEnd(FacesContext context, UIComponent component) throws IOException
+ {
+ // we don't need to do anything in here
+ }
+
+ /**
+ * @see javax.faces.render.Renderer#getRendersChildren()
+ */
+ public boolean getRendersChildren()
+ {
+ return true;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java b/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java
new file mode 100644
index 0000000000..f499fd7c1f
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/tag/DialogButtonsTag.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.ui.repo.tag;
+
+import org.alfresco.web.ui.common.tag.HtmlComponentTag;
+
+/**
+ * Tag class that allows the UIDialogButtons component to be placed on a JSP.
+ *
+ * @author gavinc
+ */
+public class DialogButtonsTag extends HtmlComponentTag
+{
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getComponentType()
+ */
+ public String getComponentType()
+ {
+ return "org.alfresco.faces.DialogButtons";
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getRendererType()
+ */
+ public String getRendererType()
+ {
+ return null;
+ }
+}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java b/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java
index 0efbc92ed8..b46d25f65f 100644
--- a/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java
+++ b/source/java/org/alfresco/web/ui/repo/tag/SystemErrorTag.java
@@ -39,6 +39,8 @@ import org.alfresco.web.bean.ErrorBean;
*/
public class SystemErrorTag extends TagSupport
{
+ private static final long serialVersionUID = -7336055169875448199L;
+
private static final String MSG_RETURN_TO_APP = "return_to_application";
private static final String MSG_HIDE_DETAILS = "hide_details";
private static final String MSG_SHOW_DETAILS = "show_details";
@@ -126,6 +128,21 @@ public class SystemErrorTag extends TagSupport
errorMessage = errorBean.getLastErrorMessage();
errorDetails = errorBean.getStackTrace();
}
+ else
+ {
+ // if we reach here the error was caught by the declaration in web.xml so
+ // pull all the information from the request and create the error bean
+ Throwable error = (Throwable)pageContext.getRequest().getAttribute("javax.servlet.error.exception");
+ String uri = (String)pageContext.getRequest().getAttribute("javax.servlet.error.request_uri");
+
+ // create and store the ErrorBean
+ errorBean = new ErrorBean();
+ pageContext.getSession().setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean);
+ errorBean.setLastError(error);
+ errorBean.setReturnPage(uri);
+ errorMessage = errorBean.getLastErrorMessage();
+ errorDetails = errorBean.getStackTrace();
+ }
try
{
@@ -254,6 +271,11 @@ public class SystemErrorTag extends TagSupport
{
throw new JspException(ioe);
}
+ finally
+ {
+ // clear out the error bean otherwise the next error could be hidden
+ pageContext.getSession().removeAttribute(ErrorBean.ERROR_BEAN_NAME);
+ }
return SKIP_BODY;
}
diff --git a/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java b/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java
new file mode 100644
index 0000000000..011ba48a96
--- /dev/null
+++ b/source/java/org/alfresco/web/ui/repo/tag/UserGroupPickerTag.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.web.ui.repo.tag;
+
+import javax.faces.component.UICommand;
+import javax.faces.component.UIComponent;
+
+import org.alfresco.web.ui.common.tag.HtmlComponentTag;
+
+/**
+ * @author Kevin Roast
+ */
+public class UserGroupPickerTag extends HtmlComponentTag
+{
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getComponentType()
+ */
+ public String getComponentType()
+ {
+ return "org.alfresco.faces.UserGroupPicker";
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#getRendererType()
+ */
+ public String getRendererType()
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
+ */
+ protected void setProperties(UIComponent component)
+ {
+ super.setProperties(component);
+
+ setStringProperty(component, "value", this.value);
+ setActionListenerProperty((UICommand)component, this.actionListener);
+ }
+
+ /**
+ * @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release()
+ */
+ public void release()
+ {
+ super.release();
+ this.value = null;
+ }
+
+ /**
+ * Set the value (binding to the list of user/group data)
+ *
+ * @param value the value
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Set the actionListener
+ *
+ * @param actionListener the actionListener
+ */
+ public void setActionListener(String actionListener)
+ {
+ this.actionListener = actionListener;
+ }
+
+ /** the value (binding to the list of user/group data) */
+ private String value;
+
+ /** the actionListener */
+ private String actionListener;
+}
diff --git a/source/java/org/jbpm/webapp/bean/AdminBean.java b/source/java/org/jbpm/webapp/bean/AdminBean.java
new file mode 100644
index 0000000000..e35540f3b7
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/AdminBean.java
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.List;
+
+import javax.faces.context.FacesContext;
+
+import org.jbpm.scheduler.impl.Scheduler;
+
+public class AdminBean {
+
+ String deployUrl;
+
+ public void deployProcess() {
+ }
+
+ public void createSchema() {
+ }
+
+ public void dropSchema() {
+ }
+
+ public boolean isSchedulerRunning() {
+ return getScheduler().isRunning();
+ }
+
+ public List getSchedulerHistoryLogs() {
+ return getScheduler().getSchedulerHistoryLogs();
+ }
+
+ private Scheduler getScheduler() {
+ return (Scheduler) FacesContext.getCurrentInstance()
+ .getExternalContext()
+ .getApplicationMap()
+ .get("scheduler");
+ }
+
+ public String getDeployUrl() {
+ return deployUrl;
+ }
+ public void setDeployUrl(String deployUrl) {
+ this.deployUrl = deployUrl;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java b/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java
new file mode 100644
index 0000000000..12b682a8ca
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/AlfrescoUserBean.java
@@ -0,0 +1,22 @@
+package org.jbpm.webapp.bean;
+
+import org.alfresco.service.cmr.security.AuthenticationService;
+
+public class AlfrescoUserBean extends UserBean
+{
+ AuthenticationService authService;
+
+
+ public void setAuthenticationService(AuthenticationService authService)
+ {
+ this.authService = authService;
+ }
+
+
+ @Override
+ public String getUserName()
+ {
+ return authService.getCurrentUserName();
+ }
+
+}
diff --git a/source/java/org/jbpm/webapp/bean/FormParameter.java b/source/java/org/jbpm/webapp/bean/FormParameter.java
new file mode 100644
index 0000000000..61ee3eb221
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/FormParameter.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import org.jbpm.context.def.VariableAccess;
+
+public class FormParameter {
+
+ private String value = null;
+ private VariableAccess variableAccess = null;
+
+ public FormParameter(String value, VariableAccess variableAccess) {
+ this.value = value;
+ this.variableAccess = variableAccess;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ public VariableAccess getVariableAccess() {
+ return variableAccess;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/HomeBean.java b/source/java/org/jbpm/webapp/bean/HomeBean.java
new file mode 100644
index 0000000000..36fedc939c
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/HomeBean.java
@@ -0,0 +1,205 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.List;
+
+import javax.faces.event.ActionEvent;
+import javax.faces.model.DataModel;
+import javax.faces.model.ListDataModel;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.jbpm.JbpmConfiguration;
+import org.jbpm.JbpmContext;
+import org.jbpm.db.GraphSession;
+import org.jbpm.db.TaskMgmtSession;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.def.Transition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.taskmgmt.def.Task;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+public class HomeBean {
+
+ UserBean userBean;
+ TaskBean taskBean;
+ JbpmConfiguration config;
+ DataModel taskInstances;
+ DataModel processDefs;
+
+
+ public HomeBean()
+ {
+ }
+
+ public void setJbpmConfiguration(JbpmConfiguration config)
+ {
+ this.config = config;
+ }
+
+ public List getTaskInstances()
+ {
+ JbpmContext xjbpmContext = config.getCurrentJbpmContext();
+ JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext;
+
+ try
+ {
+ TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
+ List taskInstances = taskMgmtSession.findTaskInstances(userBean.getUserName());
+ for (TaskInstance taskInstance : taskInstances)
+ {
+ taskInstance.getName();
+ taskInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getProcessDefinition().getName();
+ }
+ return taskInstances;
+ }
+ finally
+ {
+ if (xjbpmContext == null) jbpmContext.close();
+ }
+ }
+
+ public DataModel getTaskInstancesModel()
+ {
+ if (taskInstances == null)
+ {
+ taskInstances = new ListDataModel(getTaskInstances());
+ }
+ return taskInstances;
+ }
+
+ public List getLatestProcessDefinitions()
+ {
+ JbpmContext xjbpmContext = config.getCurrentJbpmContext();
+ JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext;
+ try
+ {
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ List procDefs = graphSession.findLatestProcessDefinitions();
+ for (ProcessDefinition procDef : procDefs)
+ {
+ procDef.getName();
+ Task startTask = procDef.getTaskMgmtDefinition().getStartTask();
+ if (startTask != null)
+ {
+ startTask.getName();
+ }
+ }
+ return procDefs;
+ }
+ finally
+ {
+ if (xjbpmContext == null) jbpmContext.close();
+ }
+ }
+
+ public DataModel getLatestProcessDefinitionsModel()
+ {
+ if (processDefs == null)
+ {
+ processDefs = new ListDataModel(getLatestProcessDefinitions());
+ }
+ return processDefs;
+ }
+
+ /**
+ * selects a task.
+ */
+ public String selectTaskInstance()
+ {
+ JbpmContext xjbpmContext = config.getCurrentJbpmContext();
+ JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext;
+
+ try
+ {
+ // Get the task instance id from request parameter
+ TaskInstance selectedTask = (TaskInstance)taskInstances.getRowData();
+ long taskInstanceId = selectedTask.getId();
+ TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
+ TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(taskInstanceId);
+ taskBean.initialize(taskInstance);
+
+ return "task";
+ }
+ finally
+ {
+ taskInstances = null;
+ processDefs = null;
+ if (xjbpmContext == null ) jbpmContext.close();
+ }
+ }
+
+ /**
+ * prepares a task form for starting a new process instance.
+ */
+ public String startProcessInstance()
+ {
+ JbpmContext xjbpmContext = config.getCurrentJbpmContext();
+ JbpmContext jbpmContext = (xjbpmContext == null) ? config.createJbpmContext() : xjbpmContext;
+ try
+ {
+ jbpmContext.setActorId(AuthenticationUtil.getCurrentUserName());
+
+ // Get the task instance id from request parameter
+ ProcessDefinition selectedProc = (ProcessDefinition)processDefs.getRowData();
+ long processDefinitionId = selectedProc.getId();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ ProcessDefinition processDefinition = graphSession.loadProcessDefinition(processDefinitionId);
+
+ // create a new process instance to run
+ ProcessInstance processInstance = new ProcessInstance(processDefinition);
+
+ // create a new taskinstance for the start task
+ Task startTask = processInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getStartTask();
+ if (startTask != null)
+ {
+ TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance();
+ taskBean.initialize(taskInstance);
+ }
+
+ // Save the process instance along with the task instance
+ jbpmContext.save(processInstance);
+
+ // Fill the task backing bean with useful information
+ return (startTask == null) ? "home" : "task";
+ }
+ finally
+ {
+ if (xjbpmContext == null) jbpmContext.close();
+ taskInstances = null;
+ processDefs = null;
+ }
+ }
+
+ public UserBean getUserBean() {
+ return userBean;
+ }
+ public void setUserBean(UserBean userBean) {
+ this.userBean = userBean;
+ }
+ public TaskBean getTaskBean() {
+ return taskBean;
+ }
+ public void setTaskBean(TaskBean taskBean) {
+ this.taskBean = taskBean;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/JsfHelper.java b/source/java/org/jbpm/webapp/bean/JsfHelper.java
new file mode 100644
index 0000000000..1d287d1b2b
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/JsfHelper.java
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.context.FacesContext;
+
+public class JsfHelper {
+
+ public static long getId(String parameterName) {
+ long value = -1;
+ String valueText = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(parameterName);
+ try {
+ Long id = new Long(valueText);
+ value = id.longValue();
+ } catch (NumberFormatException e) {
+ throw new RuntimeException("couldn't parse '"+parameterName+"'='"+valueText+"' as a long");
+ }
+ return value;
+ }
+
+ public static void addMessage(String msg) {
+ FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(msg));
+ }
+
+ public static void setSessionAttribute(String key, Object value) {
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value);
+ }
+
+ public static Object getSessionAttribute(String key) {
+ return FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
+ }
+
+ public static void removeSessionAttribute(String key) {
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(key);
+ }
+
+ public static String getParameter(String name) {
+ return (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name);
+ }
+ // private static final Log log = LogFactory.getLog(JsfHelper.class);
+}
diff --git a/source/java/org/jbpm/webapp/bean/MonitoringBean.java b/source/java/org/jbpm/webapp/bean/MonitoringBean.java
new file mode 100644
index 0000000000..2d1d254883
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/MonitoringBean.java
@@ -0,0 +1,228 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.faces.context.FacesContext;
+import javax.faces.model.SelectItem;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+
+
+/**
+ * Monitoring Bean Implementation.
+ *
+ * @author David Loiseau
+ */
+
+public class MonitoringBean {
+
+ long processInstanceId;
+ String message;
+ String variableName;
+ String variableValue;
+ String variableNameOperator;
+ String variableValueOperator;
+ ArrayList processInstances;
+
+ public String showProcessDefinitions() {
+ return "processDefinitions";
+ }
+
+ public List getProcessDefinitions() {
+
+ ArrayList processDefinitionsList = new ArrayList();
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ List processDefinitions = jbpmContext.getGraphSession().findAllProcessDefinitions();
+
+ if (processDefinitions.isEmpty() == false) {
+ ListIterator listProcessDefinitions = processDefinitions.listIterator();
+ while (listProcessDefinitions.hasNext() ) {
+ ProcessDefinition processDefinition = (ProcessDefinition)listProcessDefinitions.next();
+
+ int instancesCount = 0;
+ try {
+ Connection connection = jbpmContext.getConnection();
+ Statement statement = connection.createStatement();
+
+ String request = "SELECT COUNT(*) AS instancesCount "
+ + "FROM jbpm_processinstance "
+ + "WHERE processdefinition_='"
+ + processDefinition.getId() + "'";
+ ResultSet resultSet = statement.executeQuery(request);
+ resultSet.next();
+ instancesCount = resultSet.getInt("instancesCount");
+ }
+ catch (Exception e) {}
+
+ processDefinitionsList.add(
+ new ProcessDefinitionBean(
+ processDefinition.getId(),
+ processDefinition.getName(),
+ processDefinition.getVersion(),
+ instancesCount
+ ));
+ }
+ }
+
+ return(processDefinitionsList);
+ }
+
+ public String inspectInstance() {
+ try {
+ ProcessInstanceBean processInstanceBean = new ProcessInstanceBean(this.processInstanceId);
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean);
+ this.message = "";
+ return "inspectInstance";
+ }
+ catch (Exception exception) {
+ this.message = "Error for process instance " + this.processInstanceId;
+ return "";
+ }
+ }
+
+ public String showSearchInstances() {
+ return("showSearchInstances");
+ }
+
+ public String searchInstances() {
+
+ long count = 0;
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+
+ try {
+ Connection connection = jbpmContext.getConnection();
+ Statement statement = connection.createStatement();
+ statement.setMaxRows(100);
+
+ String request = "SELECT DISTINCT processinstance_, name_, stringvalue_ FROM jbpm_variableinstance "
+ + "WHERE name_ "
+ + this.variableNameOperator + " '"
+ + variableName + "' AND stringvalue_ "
+ + this.variableValueOperator + " '" + variableValue + "'";
+
+ ResultSet resultSet = statement.executeQuery(request);
+
+ processInstances = new ArrayList();
+
+ while (resultSet.next()) {
+ processInstances.add(new ProcessInstanceBean(
+ resultSet.getLong("processinstance_"),
+ resultSet.getString("name_"),
+ resultSet.getString("stringvalue_")));
+ count++;
+ }
+ statement.close();
+ }
+ catch (Exception e) {
+ this.message = "Search error " + e.getMessage();
+ }
+
+ if (count == 1) {
+ ProcessInstanceBean processInstanceBean = (ProcessInstanceBean)processInstances.iterator().next();
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean);
+ return("inspectInstance");
+ }
+ return "";
+ }
+
+ public List getOperatorsList (){
+
+ ArrayList operatorsList = new ArrayList();
+
+ SelectItem item = new SelectItem("=", "is equal to");
+ operatorsList.add(item);
+ item = new SelectItem("like", "is like");
+ operatorsList.add(item);
+ return operatorsList;
+
+ }
+
+ public long getProcessInstanceId() {
+ return processInstanceId;
+ }
+
+ public void setProcessInstanceId(long processInstanceId) {
+ this.processInstanceId = processInstanceId;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public boolean isShowProcessInstances() {
+ if (processInstances == null) return false;
+ if (processInstances.size() == 0) return false;
+ return true;
+ }
+
+ public ArrayList getProcessInstances() {
+ return processInstances;
+ }
+
+ public String getVariableName() {
+ return variableName;
+ }
+
+ public void setVariableName(String variableName) {
+ this.variableName = variableName;
+ }
+
+ public String getVariableValue() {
+ return variableValue;
+ }
+
+ public void setVariableValue(String variableValue) {
+ this.variableValue = variableValue;
+ }
+
+ public String getVariableNameOperator() {
+ return variableNameOperator;
+ }
+
+ public void setVariableNameOperator(String variableNameOperator) {
+ this.variableNameOperator = variableNameOperator;
+ }
+
+ public String getVariableValueOperator() {
+ return variableValueOperator;
+ }
+
+ public void setVariableValueOperator(String variableValueOperator) {
+ this.variableValueOperator = variableValueOperator;
+ }
+
+
+}
diff --git a/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java b/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java
new file mode 100644
index 0000000000..e78fa6fe09
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/ProcessDefinitionBean.java
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.faces.context.FacesContext;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.db.GraphSession;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.ProcessInstance;
+
+/**
+ * Process Definition Bean Implementation.
+ *
+ * @author David Loiseau
+ */
+
+public class ProcessDefinitionBean {
+
+ String name;
+ int version;
+ long id;
+ int instancesCount;
+
+ public ProcessDefinitionBean() {
+ }
+
+ public ProcessDefinitionBean(long id) {
+ this.id = id;
+ initialize();
+ }
+
+ public ProcessDefinitionBean(long id, String name, int version, int instancesCount) {
+ this.id = id;
+ this.name = name;
+ this.version = version;
+ this.instancesCount = instancesCount;
+ }
+
+ private void initialize() {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ ProcessDefinition processDefinition = graphSession.loadProcessDefinition(id);
+ this.name = processDefinition.getName();
+ this.version = processDefinition.getVersion();
+ this.instancesCount = graphSession.findProcessInstances(this.id).size();
+ }
+
+ public List getProcessInstances() {
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+
+ ArrayList processInstancesList = new ArrayList();
+
+ List listProcessInstance = graphSession.findProcessInstances(this.id);
+
+ if (listProcessInstance.isEmpty() == false) {
+ ListIterator listProcessInstances = listProcessInstance.listIterator();
+ while (listProcessInstances.hasNext()) {
+ ProcessInstance processInstance = (ProcessInstance) listProcessInstances.next();
+
+ processInstancesList.add(new ProcessInstanceBean(processInstance.getId(), processInstance.getStart(), processInstance.getEnd()));
+ }
+ }
+
+ return processInstancesList;
+ }
+
+ public String showProcessInstances() {
+ ProcessDefinitionBean processDefinitionBean = new ProcessDefinitionBean();
+ processDefinitionBean.setId(this.id);
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processDefinitionBean", processDefinitionBean);
+ return ("processInstances");
+ }
+
+ public String startProcessInstance() {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ ProcessDefinition processDefinition = graphSession.loadProcessDefinition(getId());
+ processDefinition.createInstance();
+ return showProcessInstances();
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ this.initialize();
+ }
+
+ public int getInstancesCount() {
+ return instancesCount;
+ }
+
+ public void setInstancesCount(int instancesCount) {
+ this.instancesCount = instancesCount;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+}
diff --git a/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java b/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java
new file mode 100644
index 0000000000..334eeb64c7
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/ProcessInstanceBean.java
@@ -0,0 +1,420 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import javax.faces.context.FacesContext;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.db.GraphSession;
+import org.jbpm.db.TaskMgmtSession;
+import org.jbpm.graph.def.Transition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.graph.exe.Token;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+/**
+ * Process Instance Bean Implementation.
+ *
+ * @author David Loiseau
+ */
+
+public class ProcessInstanceBean {
+
+ long id;
+ String processDefinitionLabel;
+ long processDefinitionId;
+ Date start;
+ Date end;
+
+ ArrayList tokens;
+ ArrayList variables;
+ ArrayList tasks;
+ ArrayList transitions;
+
+ String variableName;
+ String variableValue;
+
+ long tokenInstanceId;
+ long taskInstanceId;
+
+ public ProcessInstanceBean(long id, Date start, Date end) {
+ this.id = id;
+ this.start = start;
+ this.end = end;
+ }
+
+ public ProcessInstanceBean(long id) {
+ this.id = id;
+ this.initialize();
+ }
+
+ public ProcessInstanceBean(long id, String variableName, String variableValue) {
+ this.id = id;
+ this.variableName = variableName;
+ this.variableValue = variableValue;
+ this.initialize();
+ }
+
+ public String inspectProcessInstance() {
+ ProcessInstanceBean processInstanceBean = new ProcessInstanceBean(this.id);
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processInstanceBean", processInstanceBean);
+ return ("inspectInstance");
+ }
+
+ public String deleteProcessInstance() {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ graphSession.deleteProcessInstance(this.id);
+ return ("deleteInstance");
+ }
+
+ private void initialize() {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ ProcessInstance processInstance = graphSession.loadProcessInstance(this.id);
+ this.start = processInstance.getStart();
+ this.end = processInstance.getEnd();
+ this.processDefinitionId = processInstance.getProcessDefinition().getId();
+ this.processDefinitionLabel = processInstance.getProcessDefinition().getName() + " (version " + processInstance.getProcessDefinition().getVersion() + ")";
+
+ initializeVariablesList(processInstance);
+ initializeTokensList(processInstance);
+ initializeTasksList(processInstance);
+ }
+
+ private void initializeAvailableTransitions(TaskInstance taskInstance) {
+
+ transitions = new ArrayList();
+
+ if (taskInstance.getAvailableTransitions().isEmpty() == false) {
+ Iterator availableTransitionsIterator = taskInstance.getAvailableTransitions().iterator();
+ while (availableTransitionsIterator.hasNext()) {
+ Transition transition = (Transition) availableTransitionsIterator.next();
+ transitions.add(transition);
+
+ }
+ }
+ }
+
+ private void initializeAvailableTransitions(Token token) {
+
+ transitions = new ArrayList();
+
+ if (token.getNode().getLeavingTransitions().isEmpty() == false) {
+ Iterator availableTransitionsIterator = token.getNode().getLeavingTransitions().iterator();
+ while (availableTransitionsIterator.hasNext()) {
+ Transition transition = (Transition) availableTransitionsIterator.next();
+ transitions.add(transition);
+
+ }
+ }
+ }
+
+ private void initializeVariablesList(ProcessInstance processInstance) {
+
+ // Variables list
+ variables = new ArrayList();
+
+ if (processInstance.getContextInstance().getVariables() != null && !processInstance.getContextInstance().getVariables().values().isEmpty()) {
+ int mapsize = processInstance.getContextInstance().getVariables().size();
+ Iterator variablesIterator = processInstance.getContextInstance().getVariables().entrySet().iterator();
+ for (int i = 0; i < mapsize; i++) {
+ Entry entry = (Entry) variablesIterator.next();
+ variables.add(new VariableBean((String) entry.getKey(), entry.getValue()));
+ }
+ }
+
+ }
+
+ private void initializeTasksList(ProcessInstance processInstance) {
+
+ // Tasks list
+ tasks = new ArrayList();
+ if (processInstance.getTaskMgmtInstance().getTaskInstances().isEmpty() == false) {
+ Iterator tasksIterator = processInstance.getTaskMgmtInstance().getTaskInstances().iterator();
+ while (tasksIterator.hasNext()) {
+ TaskInstance taskInstance = (TaskInstance) tasksIterator.next();
+ tasks.add(new TaskBean(taskInstance.getId(), taskInstance.getName(), taskInstance.getActorId(), taskInstance.getEnd()));
+ }
+ }
+
+ }
+
+ private void initializeTokensList(ProcessInstance processInstance) {
+
+ // Tokens list
+ Token rootToken = processInstance.getRootToken();
+
+ tokens = new ArrayList();
+ this.tokenInstanceId = rootToken.getId();
+ this.taskInstanceId = 0;
+ tokens.add(new TokenBean(rootToken.getId(), "Root", rootToken.getNode().getName(), rootToken.getNode().getClass().getName(), rootToken.getStart(),
+ rootToken.getEnd(), 1));
+ try {
+ if (rootToken.getChildren().isEmpty() == false) {
+ AddChildrenTokensToTokensList(this.tokens, rootToken, 2);
+ }
+ } catch (Exception exception) {
+ }
+
+ }
+
+ /**
+ *
+ * Add token childs to the current token beans list
+ *
+ * @param tokensList
+ * Current token list to update
+ * @param token
+ * Token where are the token childs
+ * @param level
+ * Level where is the token: 1 for the root token, 2 for the childs
+ * of the root token, ...
+ */
+ private void AddChildrenTokensToTokensList(ArrayList tokensList, Token token, long level) {
+
+ Iterator childrenIterator = token.getChildren().values().iterator();
+ while (childrenIterator.hasNext()) {
+ Token childToken = (Token) childrenIterator.next();
+ tokensList.add(new TokenBean(childToken.getId(), childToken.getName(), childToken.getNode().getName(), childToken.getNode().getClass().getName(),
+ childToken.getStart(), childToken.getEnd(), level));
+ try {
+ if (childToken.getChildren().isEmpty() == false) {
+ AddChildrenTokensToTokensList(tokensList, childToken, level + 1);
+ }
+ } catch (Exception exception) {
+ }
+ }
+ }
+
+ public String updateVariable() {
+
+ if (this.variableName != null) {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ ProcessInstance processInstance = graphSession.loadProcessInstance(this.id);
+ if (this.variableValue != null) {
+ processInstance.getContextInstance().setVariable(this.variableName, this.variableValue);
+ } else {
+ processInstance.getContextInstance().deleteVariable(this.variableName);
+ }
+ initializeVariablesList(processInstance);
+ }
+ return "inspectInstance";
+ }
+
+ public String selectToken() {
+ this.taskInstanceId = 0;
+ this.tokenInstanceId = JsfHelper.getId("tokenInstanceId");
+ return "";
+ }
+
+ public String selectTask() {
+ this.tokenInstanceId = 0;
+ this.taskInstanceId = JsfHelper.getId("taskInstanceId");
+ return "";
+ }
+
+ public String signal() {
+
+ selectToken();
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ GraphSession graphSession = jbpmContext.getGraphSession();
+
+ Token token = graphSession.loadToken(this.tokenInstanceId);
+
+ if (token.getNode().getLeavingTransitions().size() > 1) {
+ initializeAvailableTransitions(token);
+ return "showTransitions";
+ }
+
+ token.signal();
+
+ this.initializeTokensList(token.getProcessInstance());
+
+ return "inspectInstance";
+ }
+
+ public String selectTransition() {
+ String transitionName;
+
+ transitionName = JsfHelper.getParameter("transitionName");
+ ProcessInstance processInstance = null;
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ if (this.taskInstanceId > 0) {
+ TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
+ TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(this.taskInstanceId);
+ if (transitionName.equals("")) {
+ taskInstance.end();
+ } else {
+ taskInstance.end(transitionName);
+ }
+ processInstance = taskInstance.getToken().getProcessInstance();
+ } else if (this.tokenInstanceId > 0) {
+ GraphSession graphSession = jbpmContext.getGraphSession();
+ Token token = graphSession.loadToken(this.tokenInstanceId);
+ if (transitionName.equals("")) {
+ token.signal();
+ } else {
+ token.signal(transitionName);
+ }
+ processInstance = token.getProcessInstance();
+ }
+
+ jbpmContext.save(processInstance);
+
+ this.initializeTasksList(processInstance);
+ this.initializeTokensList(processInstance);
+
+ return "inspectInstance";
+ }
+
+ public String endTask() {
+
+ selectTask();
+
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
+
+ TaskInstance taskInstance = taskMgmtSession.loadTaskInstance(this.taskInstanceId);
+
+ if (taskInstance.getAvailableTransitions().size() > 1) {
+ initializeAvailableTransitions(taskInstance);
+ return "showTransitions";
+ }
+
+ taskInstance.end();
+
+ ProcessInstance processInstance = taskInstance.getToken().getProcessInstance();
+ jbpmContext.save(processInstance);
+
+ this.initializeTasksList(processInstance);
+ this.initializeTokensList(processInstance);
+
+ return "inspectInstance";
+ }
+
+ // Show all the process instances for a given process definition ID
+ public String showProcessInstances() {
+ ProcessDefinitionBean processDefinitionBean = new ProcessDefinitionBean(this.processDefinitionId);
+ FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("processDefinitionBean", processDefinitionBean);
+ return ("processInstances");
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Date getStart() {
+ return start;
+ }
+
+ public void setStart(Date start) {
+ this.start = start;
+ }
+
+ public Date getEnd() {
+ return end;
+ }
+
+ public void setEnd(Date end) {
+ this.end = end;
+ }
+
+ public ArrayList getTokens() {
+ return tokens;
+ }
+
+ public void setTokens(ArrayList tokens) {
+ this.tokens = tokens;
+ }
+
+ public String getProcessDefinitionLabel() {
+ return processDefinitionLabel;
+ }
+
+ public void setProcessDefinitionLabel(String processDefinitionLabel) {
+ this.processDefinitionLabel = processDefinitionLabel;
+ }
+
+ public ArrayList getVariables() {
+ return variables;
+ }
+
+ public ArrayList getTasks() {
+ return tasks;
+ }
+
+ public ArrayList getTransitions() {
+ return transitions;
+ }
+
+ public void setVariables(ArrayList variables) {
+ this.variables = variables;
+ }
+
+ public String getVariableName() {
+ return variableName;
+ }
+
+ public void setVariableName(String variableName) {
+ this.variableName = variableName;
+ }
+
+ public String getVariableValue() {
+ return variableValue;
+ }
+
+ public void setVariableValue(String variableValue) {
+ this.variableValue = variableValue;
+ }
+
+ public long getTokenInstanceId() {
+ return tokenInstanceId;
+ }
+
+ public void setTokenInstanceId(long tokenInstanceId) {
+ this.taskInstanceId = 0;
+ this.tokenInstanceId = tokenInstanceId;
+ }
+
+ public long getTaskInstanceId() {
+ return taskInstanceId;
+ }
+
+ public void setTaskInstanceId(long taskInstanceId) {
+ this.tokenInstanceId = 0;
+ this.taskInstanceId = taskInstanceId;
+ }
+
+}
diff --git a/source/java/org/jbpm/webapp/bean/TaskBean.java b/source/java/org/jbpm/webapp/bean/TaskBean.java
new file mode 100644
index 0000000000..dba4ffbe5f
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/TaskBean.java
@@ -0,0 +1,269 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.faces.model.DataModel;
+import javax.faces.model.ListDataModel;
+import javax.faces.model.SelectItem;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jbpm.JbpmContext;
+import org.jbpm.context.def.VariableAccess;
+import org.jbpm.graph.def.Transition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.logging.exe.LoggingInstance;
+import org.jbpm.taskmgmt.def.TaskController;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+import org.jbpm.taskmgmt.log.TaskAssignLog;
+
+public class TaskBean {
+
+ UserBean userBean = null;
+ List taskFormParameters;
+ List availableTransitions;
+ List availableTransitionItems;
+ TaskInstance taskInstance;
+ long taskInstanceId;
+ DataModel transitions;
+
+// JbpmContext jbpmContext;
+// GraphSession graphSession;
+// TaskMgmtSession taskMgmtSession;
+
+ // For monitoring purposes
+ String name;
+ String actorId;
+ Date end;
+
+ public TaskBean() {
+// this.jbpmContext = JbpmContext.getCurrentJbpmContext();
+// this.graphSession = jbpmContext.getGraphSession();
+// this.taskMgmtSession = jbpmContext.getTaskMgmtSession();
+
+ // get the parameters from the session
+// this.taskFormParameters = (List) JsfHelper.getSessionAttribute("taskFormParameters");
+ }
+
+ public TaskBean(long taskInstanceId, String name, String actorId, Date end) {
+ this.taskInstanceId = taskInstanceId;
+ this.name = name;
+ this.actorId = actorId;
+ this.end = end;
+ }
+
+ public void initialize(TaskInstance taskInstance) {
+ this.taskInstance = taskInstance;
+ this.taskInstanceId = taskInstance.getId();
+
+ // set the parameters
+ this.taskFormParameters = new ArrayList();
+ TaskController taskController = taskInstance.getTask().getTaskController();
+ if (taskController!=null) {
+ List variableAccesses = taskController.getVariableAccesses();
+ Iterator iter = variableAccesses.iterator();
+ while (iter.hasNext()) {
+ VariableAccess variableAccess = (VariableAccess) iter.next();
+ String mappedName = variableAccess.getMappedName();
+ Object value = taskInstance.getVariable(mappedName);
+ TaskFormParameter tfp = new TaskFormParameter(variableAccess, value);
+ taskFormParameters.add(tfp);
+ }
+ }
+
+ // store the parameters in the session
+ //JsfHelper.setSessionAttribute("taskFormParameters", taskFormParameters);
+
+ // get the available transitions
+ availableTransitions = null;
+
+ availableTransitions = taskInstance.getAvailableTransitions();
+ if ((availableTransitions != null) && (availableTransitions.size() <= 1)) {
+ transitions = null;
+ availableTransitions = null;
+ availableTransitionItems = null;
+ } else {
+ transitions = new ListDataModel(availableTransitions);
+ availableTransitionItems = new ArrayList();
+ Iterator iter = availableTransitions.iterator();
+ while (iter.hasNext()) {
+ Transition transition = (Transition) iter.next();
+ SelectItem transitionItem = new SelectItem();
+ transitionItem.setValue(transition.getName());
+ transitionItem.setLabel(transition.getName());
+ transitionItem.setDisabled(false);
+ availableTransitionItems.add(transitionItem);
+ }
+ }
+
+ log.debug("initialized availableTransitions " + availableTransitions);
+ }
+
+ public String save() {
+ log.debug("saving the task parameters " + taskFormParameters);
+
+ // submit the parameters in the jbpm task controller
+ TaskInstance taskInstance = JbpmContext.getCurrentJbpmContext().getTaskMgmtSession().loadTaskInstance(taskInstanceId);
+
+ // collect the parameter values from the values that were updated in the
+ // parameters by jsf.
+ Iterator iter = taskFormParameters.iterator();
+ while (iter.hasNext()) {
+ TaskFormParameter taskFormParameter = (TaskFormParameter) iter.next();
+
+ if ((taskFormParameter.isWritable()) && (taskFormParameter.getValue() != null)) {
+ log.debug("submitting [" + taskFormParameter.getLabel() + "]=" + taskFormParameter.getValue());
+ taskInstance.setVariable(taskFormParameter.getLabel(), taskFormParameter.getValue());
+ } else {
+ log.debug("ignoring unwritable [" + taskFormParameter.getLabel() + "]");
+ }
+ }
+
+ // save the process instance and hence the updated task instance variables
+ JbpmContext.getCurrentJbpmContext().save(taskInstance);
+
+ // remove the parameters from the session
+ //JsfHelper.removeSessionAttribute("taskFormParameters");
+
+ return "home";
+ }
+
+ public String saveAndClose() {
+ // save
+ save();
+
+ TaskInstance taskInstance = JbpmContext.getCurrentJbpmContext().getTaskMgmtSession().loadTaskInstance(taskInstanceId);
+
+ // close the task instance
+ if (transitions == null)
+ {
+ taskInstance.end();
+ }
+ else
+ {
+ Transition selectedTransition = (Transition)transitions.getRowData();
+ taskInstance.end(selectedTransition.getName());
+ }
+
+ ProcessInstance processInstance = taskInstance.getTaskMgmtInstance().getProcessInstance();
+ if (processInstance.hasEnded()) {
+ JsfHelper.addMessage("The process has finished.");
+ }
+
+ LoggingInstance loggingInstance = processInstance.getLoggingInstance();
+ List assignmentLogs = loggingInstance.getLogs(TaskAssignLog.class);
+
+ log.debug("assignmentlogs: " + assignmentLogs);
+
+ if (assignmentLogs.size() == 1) {
+ TaskAssignLog taskAssignLog = (TaskAssignLog) assignmentLogs.get(0);
+ JsfHelper.addMessage("A new task has been assigned to '" + taskAssignLog.getTaskNewActorId() + "'");
+
+ } else if (assignmentLogs.size() > 1) {
+ String msg = "New tasks have been assigned to: ";
+ Iterator iter = assignmentLogs.iterator();
+ while (iter.hasNext()) {
+ TaskAssignLog taskAssignLog = (TaskAssignLog) iter.next();
+ msg += taskAssignLog.getActorId();
+ if (iter.hasNext())
+ msg += ", ";
+ }
+ msg += ".";
+ JsfHelper.addMessage(msg);
+ }
+
+ JbpmContext.getCurrentJbpmContext().save(taskInstance);
+
+ return "home";
+ }
+
+ public long getTaskInstanceId() {
+ return taskInstanceId;
+ }
+ public void setTaskInstanceId(long taskInstanceId) {
+ this.taskInstanceId = taskInstanceId;
+ }
+ public UserBean getUserBean() {
+ return userBean;
+ }
+ public void setUserBean(UserBean userBean) {
+ this.userBean = userBean;
+ }
+ public List getTaskFormParameters() {
+ return taskFormParameters;
+ }
+
+ public DataModel getTransitions()
+ {
+ return transitions;
+ }
+
+ public List getAvailableTransitions() {
+ return availableTransitions;
+ }
+ public void setAvailableTransitions(List availableTransitions) {
+ this.availableTransitions = availableTransitions;
+ }
+ public List getAvailableTransitionItems() {
+ return availableTransitionItems;
+ }
+ public TaskInstance getTaskInstance() {
+ return taskInstance;
+ }
+
+ private static final Log log = LogFactory.getLog(TaskBean.class);
+
+ public String getActorId() {
+ return actorId;
+ }
+
+ public void setActorId(String actorId) {
+ this.actorId = actorId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Date getEnd() {
+ return end;
+ }
+
+ public void setEnd(Date end) {
+ this.end = end;
+ }
+
+ public boolean isEnded() {
+ if (end == null)
+ return true;
+ return false;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/TaskFormParameter.java b/source/java/org/jbpm/webapp/bean/TaskFormParameter.java
new file mode 100644
index 0000000000..c3de976756
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/TaskFormParameter.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.io.Serializable;
+
+import org.hibernate.Session;
+import org.jbpm.context.def.VariableAccess;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+public class TaskFormParameter implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ protected String label = null;
+ protected String description = null;
+ protected Object value = null;
+ protected boolean isReadable = true;
+ protected boolean isWritable = true;
+ protected boolean isRequired = true;
+
+ public TaskFormParameter() {
+ }
+
+ public TaskFormParameter(VariableAccess variableAccess, Object value) {
+ this.label = variableAccess.getMappedName();
+ this.value = value;
+ this.isReadable = variableAccess.isReadable();
+ this.isWritable = variableAccess.isWritable();
+ this.isRequired = variableAccess.isRequired();
+ }
+
+ public TaskFormParameter(TaskFormParameter other) {
+ this.label = other.label;
+ this.description = other.description;
+ this.value = other.value;
+ this.isReadable = other.isReadable;
+ this.isWritable = other.isWritable;
+ this.isRequired = other.isRequired;
+ }
+
+ public static TaskFormParameter create(TaskInstance instance, String name, Object value, Session session) {
+ TaskFormParameter taskFormParameter = null;
+ return taskFormParameter;
+ }
+
+ public String toString() {
+ return "("+label+","+value+")";
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public boolean isReadable() {
+ return isReadable;
+ }
+ public void setReadable(boolean isReadable) {
+ this.isReadable = isReadable;
+ }
+ public boolean isRequired() {
+ return isRequired;
+ }
+ public void setRequired(boolean isRequired) {
+ this.isRequired = isRequired;
+ }
+ public boolean isWritable() {
+ return isWritable;
+ }
+ public boolean isReadOnly() {
+ return !isWritable;
+ }
+ public void setWritable(boolean isWritable) {
+ this.isWritable = isWritable;
+ }
+ public String getLabel() {
+ return label;
+ }
+ public void setLabel(String label) {
+ this.label = label;
+ }
+ public Object getValue() {
+ return value;
+ }
+ public void setValue(Object value) {
+ this.value = value;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/TokenBean.java b/source/java/org/jbpm/webapp/bean/TokenBean.java
new file mode 100644
index 0000000000..ba85f8c1a0
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/TokenBean.java
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.Date;
+
+/**
+ * Token Bean Implementation.
+ *
+ * @author David Loiseau
+ */
+
+public class TokenBean {
+
+ long id;
+ String name;
+ String nodeName;
+ String nodeClassName;
+ Date start;
+ Date end;
+ long level;
+
+ public TokenBean(long id, String name, String nodeName, String nodeClassName, Date start, Date end, long level) {
+
+ this.id = id;
+ this.name = name;
+ this.nodeName = nodeName;
+ this.nodeClassName = nodeClassName;
+ this.start = start;
+ this.end = end;
+ this.level = level;
+ }
+
+ private String getTypeNameFromClassName(String className) {
+ String typeName = "";
+ if (className.indexOf(".") > 0) {
+ typeName = className.substring(className.lastIndexOf(".") + 1);
+ }
+ return typeName;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLabel() {
+ String label = "";
+ int i = 1;
+ while (i < this.level) {
+ label = label + "---";
+ i++;
+ }
+ if (i > 1)
+ label = label + " ";
+ label = label + this.name;
+
+ return label;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public Date getEnd() {
+ return end;
+ }
+
+ public void setEnd(Date end) {
+ this.end = end;
+ }
+
+ public Date getStart() {
+ return start;
+ }
+
+ public void setStart(Date start) {
+ this.start = start;
+ }
+
+ public String getNodeType() {
+ return getTypeNameFromClassName(this.nodeClassName);
+ }
+
+ public boolean isSignal() {
+ if (this.end == null)
+ return true;
+ return false;
+ }
+
+}
diff --git a/source/java/org/jbpm/webapp/bean/UserBean.java b/source/java/org/jbpm/webapp/bean/UserBean.java
new file mode 100644
index 0000000000..f5ecd333e7
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/UserBean.java
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.faces.model.SelectItem;
+
+import org.hibernate.Session;
+import org.jbpm.JbpmContext;
+import org.jbpm.identity.User;
+import org.jbpm.identity.hibernate.IdentitySession;
+
+public class UserBean {
+
+ String userName;
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String name) {
+ this.userName = name;
+ }
+
+ public String login() {
+ JbpmContext.getCurrentJbpmContext().setActorId(userName);
+ return "home";
+ }
+
+ public List getUsers() {
+ Session session = JbpmContext.getCurrentJbpmContext().getSession();
+ IdentitySession identitySession = new IdentitySession(session);
+ return identitySession.getUsers();
+ }
+
+ public List getUserSelectItems() {
+ List userSelectItems = new ArrayList();
+
+ Iterator iter = getUsers().iterator();
+ while (iter.hasNext()) {
+ User user = (User) iter.next();
+ userSelectItems.add(new UserSelectItem(user));
+ }
+
+ return userSelectItems;
+ }
+
+ public static class UserSelectItem extends SelectItem {
+ private static final long serialVersionUID = 1L;
+ public UserSelectItem(User user) {
+ setValue(user.getName());
+ setLabel(user.getName());
+ }
+ }
+}
diff --git a/source/java/org/jbpm/webapp/bean/VariableBean.java b/source/java/org/jbpm/webapp/bean/VariableBean.java
new file mode 100644
index 0000000000..eefd7b2500
--- /dev/null
+++ b/source/java/org/jbpm/webapp/bean/VariableBean.java
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.bean;
+
+/**
+ * Variable Bean Implementation.
+ *
+ * @author David Loiseau
+ */
+
+public class VariableBean {
+
+ String name;
+ Object value;
+
+ public VariableBean(String name, Object value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/context/BpmContext.java b/source/java/org/jbpm/webapp/context/BpmContext.java
new file mode 100644
index 0000000000..c8c18babdf
--- /dev/null
+++ b/source/java/org/jbpm/webapp/context/BpmContext.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.context;
+
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+public class BpmContext {
+
+ ProcessDefinition processDefinition;
+ TaskInstance taskInstance;
+
+ public ProcessDefinition getProcessDefinition() {
+ return processDefinition;
+ }
+ public void setProcessDefinition(ProcessDefinition processDefinition) {
+ this.processDefinition = processDefinition;
+ }
+ public TaskInstance getTaskInstance() {
+ return taskInstance;
+ }
+ public void setTaskInstance(TaskInstance taskInstance) {
+ this.taskInstance = taskInstance;
+ }
+}
diff --git a/source/java/org/jbpm/webapp/context/Context.java b/source/java/org/jbpm/webapp/context/Context.java
new file mode 100644
index 0000000000..89b54a1840
--- /dev/null
+++ b/source/java/org/jbpm/webapp/context/Context.java
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.context;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Context {
+
+ static ThreadLocal contextsThreadLocal = new ThreadLocal();
+
+ public static void create() {
+ contextsThreadLocal.set(new HashMap());
+ }
+
+ public static void destroy() {
+ contextsThreadLocal.set(null);
+ }
+
+ public static Object getContext(Class clazz) {
+ Map contexts = (Map) contextsThreadLocal.get();
+ Object context = contexts.get(clazz);
+ if (context==null) {
+ try {
+ context = clazz.newInstance();
+ contexts.put(clazz, context);
+ } catch (Exception e) {
+ throw new RuntimeException("couldn't instantiate context '"+clazz.getName()+"'");
+ }
+ }
+ return context;
+ }
+
+ public static PersistenceContext getPersistenceContext() {
+ return (PersistenceContext) getContext(PersistenceContext.class);
+ }
+
+ public static BpmContext getBpmContext() {
+ return (BpmContext) getContext(BpmContext.class);
+ }
+}
diff --git a/source/java/org/jbpm/webapp/context/PersistenceContext.java b/source/java/org/jbpm/webapp/context/PersistenceContext.java
new file mode 100644
index 0000000000..0cb090c8d6
--- /dev/null
+++ b/source/java/org/jbpm/webapp/context/PersistenceContext.java
@@ -0,0 +1,83 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.context;
+
+import javax.naming.InitialContext;
+import javax.rmi.PortableRemoteObject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.SessionFactory;
+import org.jbpm.db.JbpmSession;
+import org.jbpm.db.JbpmSessionFactory;
+import org.jbpm.identity.hibernate.IdentitySession;
+
+public class PersistenceContext {
+
+ static String jndiName = "java:/jbpm/SessionFactory";
+ static JbpmSessionFactory jbpmSessionFactory = null;
+ static {
+ try {
+ InitialContext initialContext = new InitialContext();
+ Object o = initialContext.lookup(jndiName);
+ SessionFactory sessionFactory = (SessionFactory) PortableRemoteObject.narrow(o, SessionFactory.class);
+ jbpmSessionFactory = new JbpmSessionFactory(null, sessionFactory);
+ } catch (Exception e) {
+ throw new RuntimeException("couldn't get the hibernate session factory from jndi entry '"+jndiName+"'", e);
+ }
+ }
+
+ boolean isRollbackOnly;
+ JbpmSession jbpmSession;
+ IdentitySession identitySession;
+
+ public void beginTransaction() {
+ isRollbackOnly = false;
+ log.debug("beginning transaction");
+ jbpmSession = jbpmSessionFactory.openJbpmSessionAndBeginTransaction();
+ identitySession = new IdentitySession(jbpmSession.getSession());
+ }
+
+ public void endTransaction() {
+ if (isRollbackOnly) {
+ log.debug("rolling back transaction");
+ jbpmSession.rollbackTransactionAndClose();
+ } else {
+ log.debug("committing transaction");
+ jbpmSession.commitTransactionAndClose();
+ }
+ }
+
+ public void setRollbackOnly() {
+ isRollbackOnly = true;
+ }
+
+ public IdentitySession getIdentitySession() {
+ return identitySession;
+ }
+
+ public JbpmSession getJbpmSession() {
+ return jbpmSession;
+ }
+
+ private static final Log log = LogFactory.getLog(PersistenceContext.class);
+}
diff --git a/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java b/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java
new file mode 100644
index 0000000000..4f93dac993
--- /dev/null
+++ b/source/java/org/jbpm/webapp/filter/AuthenticationFilter.java
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.filter;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.webapp.bean.UserBean;
+
+public class AuthenticationFilter implements Filter {
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpSession session = request.getSession();
+ UserBean userBean = null;
+ if (session!=null) {
+ userBean = (UserBean) session.getAttribute("userBean");
+ }
+ if (userBean!=null) {
+ String actorId = userBean.getUserName();
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ jbpmContext.setActorId(actorId);
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ public void destroy() {
+ }
+}
diff --git a/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java b/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java
new file mode 100644
index 0000000000..b2ab089369
--- /dev/null
+++ b/source/java/org/jbpm/webapp/filter/JbpmContextFilter.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.filter;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.TransactionUtil;
+import org.alfresco.service.transaction.TransactionService;
+import org.jbpm.JbpmConfiguration;
+import org.jbpm.JbpmContext;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+public class JbpmContextFilter implements Filter, Serializable
+{
+
+ private static final long serialVersionUID = 1L;
+
+ private ServletContext context;
+
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ this.context = filterConfig.getServletContext();
+ }
+
+ public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain)
+ throws IOException, ServletException
+ {
+ WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
+ final JbpmConfiguration jbpmConfig = (JbpmConfiguration) wc.getBean("jbpm_configuration");
+ TransactionService trx = (TransactionService) wc.getBean("TransactionService");
+
+ TransactionUtil.executeInUserTransaction(trx, new TransactionUtil.TransactionWork()
+ {
+ public Object doWork() throws Exception
+ {
+ JbpmContext jbpmContext = jbpmConfig.createJbpmContext();
+ try
+ {
+ String actorId = AuthenticationUtil.getCurrentUserName();
+ if (actorId != null)
+ {
+ jbpmContext.setActorId(actorId);
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ finally
+ {
+ jbpmContext.close();
+ }
+ return null;
+ }
+ });
+ }
+
+ public void destroy()
+ {
+ }
+}
diff --git a/source/java/org/jbpm/webapp/filter/LogFilter.java b/source/java/org/jbpm/webapp/filter/LogFilter.java
new file mode 100644
index 0000000000..4cfa8db033
--- /dev/null
+++ b/source/java/org/jbpm/webapp/filter/LogFilter.java
@@ -0,0 +1,69 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.filter;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class LogFilter implements Filter {
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+
+ log.debug("request "+request.getRequestURL());
+
+ Enumeration enumeration = request.getParameterNames();
+ while (enumeration.hasMoreElements()) {
+ String paramName = (String) enumeration.nextElement();
+ log.debug("request parameter ["+paramName+"]="+request.getParameter(paramName));
+ }
+
+ HttpSession session = request.getSession();
+ enumeration = session.getAttributeNames();
+ while (enumeration.hasMoreElements()) {
+ String attributeName = (String) enumeration.nextElement();
+ log.debug("session parameter ["+attributeName+"]="+session.getAttribute(attributeName));
+ }
+
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+
+ private static final Log log = LogFactory.getLog(LogFilter.class);
+}
diff --git a/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java b/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java
new file mode 100644
index 0000000000..85146b8d8d
--- /dev/null
+++ b/source/java/org/jbpm/webapp/servlet/ProcessImageServlet.java
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.servlet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jbpm.JbpmContext;
+import org.jbpm.graph.def.ProcessDefinition;
+
+public class ProcessImageServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ long processDefinitionId = Long.parseLong( request.getParameter( "definitionId" ) );
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ ProcessDefinition processDefinition = jbpmContext.getGraphSession().loadProcessDefinition(processDefinitionId);
+ byte[] bytes = processDefinition.getFileDefinition().getBytes("processimage.jpg");
+ OutputStream out = response.getOutputStream();
+ out.write(bytes);
+ out.flush();
+
+ // leave this in. it is in case we want to set the mime type later.
+ // get the mime type
+ // String contentType = URLConnection.getFileNameMap().getContentTypeFor( fileName );
+ // set the content type (=mime type)
+ // response.setContentType( contentType );
+ }
+}
diff --git a/source/java/org/jbpm/webapp/tag/ProcessImageTag.java b/source/java/org/jbpm/webapp/tag/ProcessImageTag.java
new file mode 100644
index 0000000000..5eaf9ffd63
--- /dev/null
+++ b/source/java/org/jbpm/webapp/tag/ProcessImageTag.java
@@ -0,0 +1,244 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jbpm.webapp.tag;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.tagext.TagSupport;
+
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.XPath;
+import org.dom4j.xpath.DefaultXPath;
+import org.jbpm.JbpmContext;
+import org.jbpm.file.def.FileDefinition;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.exe.Token;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+
+public class ProcessImageTag extends TagSupport {
+
+ private static final long serialVersionUID = 1L;
+ private long taskInstanceId = -1;
+ private long tokenInstanceId = -1;
+
+ private byte[] gpdBytes = null;
+ private byte[] imageBytes = null;
+ private Token currentToken = null;
+ private ProcessDefinition processDefinition = null;
+
+ static String currentTokenColor = "red";
+ static String childTokenColor = "blue";
+ static String tokenNameColor = "blue";
+
+
+ public void release() {
+ taskInstanceId = -1;
+ gpdBytes = null;
+ imageBytes = null;
+ currentToken = null;
+ }
+
+ public int doEndTag() throws JspException {
+ try {
+ initialize();
+ retrieveByteArrays();
+ if (gpdBytes != null && imageBytes != null) {
+ writeTable();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new JspException("table couldn't be displayed", e);
+ } catch (DocumentException e) {
+ e.printStackTrace();
+ throw new JspException("table couldn't be displayed", e);
+ }
+ release();
+ return EVAL_PAGE;
+ }
+
+ private void retrieveByteArrays() {
+ try {
+ FileDefinition fileDefinition = processDefinition.getFileDefinition();
+ gpdBytes = fileDefinition.getBytes("gpd.xml");
+ imageBytes = fileDefinition.getBytes("processimage.jpg");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void writeTable() throws IOException, DocumentException {
+
+ int borderWidth = 4;
+ Element rootDiagramElement = DocumentHelper.parseText(new String(gpdBytes)).getRootElement();
+ int[] boxConstraint;
+ int[] imageDimension = extractImageDimension(rootDiagramElement);
+ String imageLink = "/alfresco/processimage?definitionId=" + processDefinition.getId();
+ JspWriter jspOut = pageContext.getOut();
+
+ if (tokenInstanceId > 0) {
+
+ List allTokens = new ArrayList();
+ walkTokens(currentToken, allTokens);
+
+ jspOut.println("");
+
+ for (int i = 0; i < allTokens.size(); i++)
+ {
+ Token token = (Token) allTokens.get(i);
+
+ //check how many tokens are on teh same level (= having the same parent)
+ int offset = i;
+ if(i > 0) {
+ while(offset > 0 && ((Token) allTokens.get(offset - 1)).getParent().equals(token.getParent())) {
+ offset--;
+ }
+ }
+ boxConstraint = extractBoxConstraint(rootDiagramElement, token);
+
+ //Adjust for borders
+ //boxConstraint[2]-=borderWidth*2;
+ //boxConstraint[3]-=borderWidth*2;
+
+ jspOut.println("
");
+
+ if(token.getName()!=null)
+ {
+ jspOut.println(" " + token.getName() +" ");
+ }
+
+ jspOut.println("
");
+ }
+ jspOut.println("
");
+ }
+ else
+ {
+ boxConstraint = extractBoxConstraint(rootDiagramElement);
+
+ jspOut.println("");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println("
");
+ jspOut.println(" ");
+ jspOut.println(" ");
+ jspOut.println("
");
+ }
+ }
+
+ private int[] extractBoxConstraint(Element root) {
+ int[] result = new int[4];
+ String nodeName = currentToken.getNode().getName();
+ XPath xPath = new DefaultXPath("//node[@name='" + nodeName + "']");
+ Element node = (Element) xPath.selectSingleNode(root);
+ result[0] = Integer.valueOf(node.attribute("x").getValue()).intValue();
+ result[1] = Integer.valueOf(node.attribute("y").getValue()).intValue();
+ result[2] = Integer.valueOf(node.attribute("width").getValue()).intValue();
+ result[3] = Integer.valueOf(node.attribute("height").getValue()).intValue();
+ return result;
+ }
+
+ private int[] extractBoxConstraint(Element root, Token token) {
+ int[] result = new int[4];
+ String nodeName = token.getNode().getName();
+ XPath xPath = new DefaultXPath("//node[@name='" + nodeName + "']");
+ Element node = (Element) xPath.selectSingleNode(root);
+ result[0] = Integer.valueOf(node.attribute("x").getValue()).intValue();
+ result[1] = Integer.valueOf(node.attribute("y").getValue()).intValue();
+ result[2] = Integer.valueOf(node.attribute("width").getValue()).intValue();
+ result[3] = Integer.valueOf(node.attribute("height").getValue()).intValue();
+ return result;
+ }
+
+ private int[] extractImageDimension(Element root) {
+ int[] result = new int[2];
+ result[0] = Integer.valueOf(root.attribute("width").getValue()).intValue();
+ result[1] = Integer.valueOf(root.attribute("height").getValue()).intValue();
+ return result;
+ }
+
+ private void initialize() {
+ JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
+ if (this.taskInstanceId > 0) {
+ TaskInstance taskInstance = jbpmContext.getTaskMgmtSession().loadTaskInstance(taskInstanceId);
+ currentToken = taskInstance.getToken();
+ }
+ else
+ {
+ if (this.tokenInstanceId > 0)
+ currentToken = jbpmContext.getGraphSession().loadToken(this.tokenInstanceId);
+ }
+ processDefinition = currentToken.getProcessInstance().getProcessDefinition();
+ }
+
+ private void walkTokens(Token parent, List allTokens)
+ {
+ Map children = parent.getChildren();
+ if(children != null && children.size() > 0)
+ {
+ Collection childTokens = children.values();
+ for (Iterator iterator = childTokens.iterator(); iterator.hasNext();)
+ {
+ Token child = (Token) iterator.next();
+ walkTokens(child, allTokens);
+ }
+ }
+
+ allTokens.add(parent);
+ }
+
+ public void setTask(long id) {
+ this.taskInstanceId = id;
+ }
+
+ public void setToken(long id) {
+ this.tokenInstanceId = id;
+ }
+
+}
diff --git a/source/web/WEB-INF/alfresco.tld b/source/web/WEB-INF/alfresco.tld
index cf592f757b..cee3690833 100644
--- a/source/web/WEB-INF/alfresco.tld
+++ b/source/web/WEB-INF/alfresco.tld
@@ -1693,6 +1693,12 @@
false
true
+
+
+ multiSelect
+ false
+ true
+
addButtonLabel
diff --git a/source/web/WEB-INF/c.tld b/source/web/WEB-INF/c.tld
new file mode 100644
index 0000000000..22698c97dc
--- /dev/null
+++ b/source/web/WEB-INF/c.tld
@@ -0,0 +1,563 @@
+
+
+
+
+ JSTL 1.1 core library
+ JSTL core
+ 1.1
+ c
+ http://java.sun.com/jsp/jstl/core
+
+
+
+ Provides core validation features for JSTL tags.
+
+
+ org.apache.taglibs.standard.tlv.JstlCoreTLV
+
+
+
+
+
+ Catches any Throwable that occurs in its body and optionally
+ exposes it.
+
+ catch
+ org.apache.taglibs.standard.tag.common.core.CatchTag
+ JSP
+
+
+Name of the exported scoped variable for the
+exception thrown from a nested action. The type of the
+scoped variable is the type of the exception thrown.
+
+ var
+ false
+ false
+
+
+
+
+
+ Simple conditional tag that establishes a context for
+ mutually exclusive conditional operations, marked by
+ <when> and <otherwise>
+
+ choose
+ org.apache.taglibs.standard.tag.common.core.ChooseTag
+ JSP
+
+
+
+
+ Simple conditional tag, which evalutes its body if the
+ supplied condition is true and optionally exposes a Boolean
+ scripting variable representing the evaluation of this condition
+
+ if
+ org.apache.taglibs.standard.tag.rt.core.IfTag
+ JSP
+
+
+The test condition that determines whether or
+not the body content should be processed.
+
+ test
+ true
+ true
+ boolean
+
+
+
+Name of the exported scoped variable for the
+resulting value of the test condition. The type
+of the scoped variable is Boolean.
+
+ var
+ false
+ false
+
+
+
+Scope for var.
+
+ scope
+ false
+ false
+
+
+
+
+
+ Retrieves an absolute or relative URL and exposes its contents
+ to either the page, a String in 'var', or a Reader in 'varReader'.
+
+ import
+ org.apache.taglibs.standard.tag.rt.core.ImportTag
+ org.apache.taglibs.standard.tei.ImportTEI
+ JSP
+
+
+The URL of the resource to import.
+
+ url
+ true
+ true
+
+
+
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is String.
+
+ var
+ false
+ false
+
+
+
+Scope for var.
+
+ scope
+ false
+ false
+
+
+
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is Reader.
+
+ varReader
+ false
+ false
+
+
+
+Name of the context when accessing a relative
+URL resource that belongs to a foreign
+context.
+
+ context
+ false
+ true
+
+
+
+Character encoding of the content at the input
+resource.
+
+ charEncoding
+ false
+ true
+
+
+
+
+
+ The basic iteration tag, accepting many different
+ collection types and supporting subsetting and other
+ functionality
+
+ forEach
+ org.apache.taglibs.standard.tag.rt.core.ForEachTag
+ org.apache.taglibs.standard.tei.ForEachTEI
+ JSP
+
+
+Collection of items to iterate over.
+
+ items
+ false
+ true
+ java.lang.Object
+
+
+
+If items specified:
+Iteration begins at the item located at the
+specified index. First item of the collection has
+index 0.
+If items not specified:
+Iteration begins with index set at the value
+specified.
+
+ begin
+ false
+ true
+ int
+
+
+
+If items specified:
+Iteration ends at the item located at the
+specified index (inclusive).
+If items not specified:
+Iteration ends when index reaches the value
+specified.
+
+ end
+ false
+ true
+ int
+
+
+
+Iteration will only process every step items of
+the collection, starting with the first one.
+
+ step
+ false
+ true
+ int
+
+
+
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility. Its type depends
+on the object of the underlying collection.
+
+ var
+ false
+ false
+
+
+
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of type
+javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested
+visibility.
+
+ varStatus
+ false
+ false
+
+
+
+
+
+ Iterates over tokens, separated by the supplied delimeters
+
+ forTokens
+ org.apache.taglibs.standard.tag.rt.core.ForTokensTag
+ JSP
+
+
+String of tokens to iterate over.
+
+ items
+ true
+ true
+ java.lang.String
+
+
+
+The set of delimiters (the characters that
+separate the tokens in the string).
+
+ delims
+ true
+ true
+ java.lang.String
+
+
+
+Iteration begins at the token located at the
+specified index. First token has index 0.
+
+ begin
+ false
+ true
+ int
+
+
+
+Iteration ends at the token located at the
+specified index (inclusive).
+
+ end
+ false
+ true
+ int
+
+
+
+Iteration will only process every step tokens
+of the string, starting with the first one.
+
+ step
+ false
+ true
+ int
+
+
+
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility.
+
+ var
+ false
+ false
+
+
+
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of
+type
+javax.servlet.jsp.jstl.core.LoopTag
+Status. This scoped variable has nested
+visibility.
+
+ varStatus
+ false
+ false
+
+
+
+
+
+ Like <%= ... >, but for expressions.
+
+ out
+ org.apache.taglibs.standard.tag.rt.core.OutTag
+ JSP
+
+
+Expression to be evaluated.
+
+ value
+ true
+ true
+
+
+
+Default value if the resulting value is null.
+
+ default
+ false
+ true
+
+
+
+Determines whether characters <,>,&,'," in the
+resulting string should be converted to their
+corresponding character entity codes. Default value is
+true.
+
+ escapeXml
+ false
+ true
+
+
+
+
+
+
+ Subtag of <choose> that follows <when> tags
+ and runs only if all of the prior conditions evaluated to
+ 'false'
+
+ otherwise
+ org.apache.taglibs.standard.tag.common.core.OtherwiseTag
+ JSP
+
+
+
+
+ Adds a parameter to a containing 'import' tag's URL.
+
+ param
+ org.apache.taglibs.standard.tag.rt.core.ParamTag
+ JSP
+
+
+Name of the query string parameter.
+
+ name
+ true
+ true
+
+
+
+Value of the parameter.
+
+ value
+ false
+ true
+
+
+
+
+
+ Redirects to a new URL.
+
+ redirect
+ org.apache.taglibs.standard.tag.rt.core.RedirectTag
+ JSP
+
+
+The URL of the resource to redirect to.
+
+ url
+ false
+ true
+
+
+
+Name of the context when redirecting to a relative URL
+resource that belongs to a foreign context.
+
+ context
+ false
+ true
+
+
+
+
+
+ Removes a scoped variable (from a particular scope, if specified).
+
+ remove
+ org.apache.taglibs.standard.tag.common.core.RemoveTag
+ empty
+
+
+Name of the scoped variable to be removed.
+
+ var
+ true
+ false
+
+
+
+Scope for var.
+
+ scope
+ false
+ false
+
+
+
+
+
+ Sets the result of an expression evaluation in a 'scope'
+
+ set
+ org.apache.taglibs.standard.tag.rt.core.SetTag
+ JSP
+
+
+Name of the exported scoped variable to hold the value
+specified in the action. The type of the scoped variable is
+whatever type the value expression evaluates to.
+
+ var
+ false
+ false
+
+
+
+Expression to be evaluated.
+
+ value
+ false
+ true
+
+
+
+Target object whose property will be set. Must evaluate to
+a JavaBeans object with setter property property, or to a
+java.util.Map object.
+
+ target
+ false
+ true
+
+
+
+Name of the property to be set in the target object.
+
+ property
+ false
+ true
+
+
+
+Scope for var.
+
+ scope
+ false
+ false
+
+
+
+
+
+ Creates a URL with optional query parameters.
+
+ url
+ org.apache.taglibs.standard.tag.rt.core.UrlTag
+ JSP
+
+
+Name of the exported scoped variable for the
+processed url. The type of the scoped variable is
+String.
+
+ var
+ false
+ false
+
+
+
+Scope for var.
+
+ scope
+ false
+ false
+
+
+
+URL to be processed.
+
+ value
+ false
+ true
+
+
+
+Name of the context when specifying a relative URL
+resource that belongs to a foreign context.
+
+ context
+ false
+ true
+
+
+
+
+
+ Subtag of <choose> that includes its body if its
+ condition evalutes to 'true'
+
+ when
+ org.apache.taglibs.standard.tag.rt.core.WhenTag
+ JSP
+
+
+The test condition that determines whether or not the
+body content should be processed.
+
+ test
+ true
+ true
+ boolean
+
+
+
+
diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml
index f1be25f9a3..fb26e4ff66 100644
--- a/source/web/WEB-INF/faces-config-beans.xml
+++ b/source/web/WEB-INF/faces-config-beans.xml
@@ -95,6 +95,17 @@
+
+ Bean that provides information for the About page
+ AboutBean
+ org.alfresco.web.bean.AboutBean
+ session
+
+ descriptorService
+ #{DescriptorService}
+
+
+
Bean that manages the dialog framework
DialogManager
@@ -239,6 +250,10 @@
copyService
#{CopyService}
+
+ searchService
+ #{SearchService}
+
@@ -543,6 +558,31 @@
+
+
+ The bean that backs up the Set Content Properties Dialog
+
+ SetContentPropertiesDialog
+ org.alfresco.web.bean.content.SetContentPropertiesDialog
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ fileFolderService
+ #{FileFolderService}
+
+
+ dictionaryService
+ #{DictionaryService}
+
+
+ browseBean
+ #{BrowseBean}
+
+
+
The bean that backs up the Edit Content Properties Dialog
@@ -1030,6 +1070,10 @@
actionService
#{ActionService}
+
+ nodeService
+ #{NodeService}
+
@@ -1507,8 +1551,8 @@
#{ContentService}
- namespaceService
- #{NamespaceService}
+ dictionaryService
+ #{DictionaryService}
@@ -1614,8 +1658,8 @@
#{BrowseBean}
- dictionaryService
- #{DictionaryService}
+ namespaceService
+ #{NamespaceService}
namespaceService
@@ -1680,8 +1724,8 @@
#{SearchService}
- dictionaryService
- #{DictionaryService}
+ namespaceService
+ #{NamespaceService}
@@ -1719,6 +1763,242 @@
+
+
+ The bean that backs up the Manage WorkItem Dialog
+
+ ManageWorkItemDialog
+ org.alfresco.web.bean.workflow.ManageWorkItemDialog
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ fileFolderService
+ #{FileFolderService}
+
+
+ searchService
+ #{SearchService}
+
+
+ navigator
+ #{NavigationBean}
+
+
+ browseBean
+ #{BrowseBean}
+
+
+ dictionaryService
+ #{DictionaryService}
+
+
+ namespaceService
+ #{NamespaceService}
+
+
+ workflowService
+ #{WorkflowService}
+
+
+
+
+ StartWorkflowWizard
+ org.alfresco.web.bean.workflow.StartWorkflowWizard
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ fileFolderService
+ #{FileFolderService}
+
+
+ searchService
+ #{SearchService}
+
+
+ navigator
+ #{NavigationBean}
+
+
+ browseBean
+ #{BrowseBean}
+
+
+ namespaceService
+ #{NamespaceService}
+
+
+ namespaceService
+ #{NamespaceService}
+
+
+ workflowService
+ #{WorkflowService}
+
+
+
+
+
+ The bean that backs up the Cancel Workflow Dialog
+
+ CancelWorkflowDialog
+ org.alfresco.web.bean.workflow.CancelWorkflowDialog
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ fileFolderService
+ #{FileFolderService}
+
+
+ searchService
+ #{SearchService}
+
+
+ navigator
+ #{NavigationBean}
+
+
+ browseBean
+ #{BrowseBean}
+
+
+ dictionaryService
+ #{DictionaryService}
+
+
+ namespaceService
+ #{NamespaceService}
+
+
+ workflowService
+ #{WorkflowService}
+
+
+
+
+
+ The bean that backs up the Reassign Work Item Dialog
+
+ ReassignWorkItemDialog
+ org.alfresco.web.bean.workflow.ReassignWorkItemDialog
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ fileFolderService
+ #{FileFolderService}
+
+
+ searchService
+ #{SearchService}
+
+
+ navigator
+ #{NavigationBean}
+
+
+ browseBean
+ #{BrowseBean}
+
+
+ dictionaryService
+ #{DictionaryService}
+
+
+ namespaceService
+ #{NamespaceService}
+
+
+ workflowService
+ #{WorkflowService}
+
+
+ personService
+ #{PersonService}
+
+
+
+
+ WorkflowBean
+ org.alfresco.web.bean.workflow.WorkflowBean
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ workflowService
+ #{WorkflowService}
+
+
+
+
+
+ The bean that backs up the Email Space Users Dialog
+
+ EmailSpaceUsersDialog
+ org.alfresco.web.bean.users.EmailSpaceUsersDialog
+ session
+
+ browseBean
+ #{BrowseBean}
+
+
+ nodeService
+ #{NodeService}
+
+
+ permissionService
+ #{PermissionService}
+
+
+ personService
+ #{PersonService}
+
+
+ authorityService
+ #{AuthorityService}
+
+
+ mailSender
+ #{mailService}
+
+
+
+
+ Bean that manages the Dashboard framework
+ DashboardManager
+ org.alfresco.web.bean.dashboard.DashboardManager
+ session
+
+
+
+
+ The bean that backs up the Dashboard Config Wizard
+
+ DashboardWizard
+ org.alfresco.web.bean.dashboard.DashboardWizard
+ session
+
+ nodeService
+ #{NodeService}
+
+
+ dashboardManager
+ #{DashboardManager}
+
+
+
@@ -1728,6 +2008,31 @@
TextFieldGenerator
org.alfresco.web.bean.generator.TextFieldGenerator
request
+
+
+
+
+
+ Bean that generates a text area component
+
+ TextAreaGenerator
+ org.alfresco.web.bean.generator.TextAreaGenerator
+ request
+
@@ -1746,6 +2051,16 @@
DatePickerGenerator
org.alfresco.web.bean.generator.DatePickerGenerator
request
+
@@ -1755,6 +2070,16 @@
DateTimePickerGenerator
org.alfresco.web.bean.generator.DateTimePickerGenerator
request
+
@@ -1809,6 +2134,44 @@
ChildAssociationGenerator
org.alfresco.web.bean.generator.ChildAssociationGenerator
request
+
+
+
+
+ Bean that generates a separator component
+
+ SeparatorGenerator
+ org.alfresco.web.bean.generator.SeparatorGenerator
+ request
+
+
+
+
+ Bean that generates a header separator component
+
+ HeaderSeparatorGenerator
+ org.alfresco.web.bean.generator.HeaderSeparatorGenerator
+ request
+
+
+
+
+
+
+
+ Bean that returns information on a node
+
+ NodeInfoBean
+ org.alfresco.web.bean.ajax.NodeInfoBean
+ request
+
+ nodeService
+ #{NodeService}
+
+
+ contentService
+ #{ContentService}
+
diff --git a/source/web/WEB-INF/faces-config-jbpm.xml b/source/web/WEB-INF/faces-config-jbpm.xml
new file mode 100644
index 0000000000..e31eed4112
--- /dev/null
+++ b/source/web/WEB-INF/faces-config-jbpm.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+ userBean
+ org.jbpm.webapp.bean.AlfrescoUserBean
+ session
+
+ authenticationService
+ #{AuthenticationService}
+
+
+
+
+ homeBean
+ org.jbpm.webapp.bean.HomeBean
+ session
+
+ taskBean
+ #{taskBean}
+
+
+ userBean
+ #{userBean}
+
+
+ jbpmConfiguration
+ #{jbpm_configuration}
+
+
+
+
+ taskBean
+ org.jbpm.webapp.bean.TaskBean
+ session
+
+ userBean
+ #{userBean}
+
+
+
+
+ monitoringBean
+ org.jbpm.webapp.bean.MonitoringBean
+ session
+
+
+
+ /jbpm/login.jsp
+
+ home
+ /jbpm/home.jsp
+
+
+
+
+ /jbpm/home.jsp
+
+ login
+ /jbpm/login.jsp
+
+
+
+
+ /jbpm/home.jsp
+
+ task
+ /jbpm/task.jsp
+
+
+
+
+ /jbpm/task.jsp
+
+ login
+ /jbpm/login.jsp
+
+
+
+
+ /jbpm/task.jsp
+
+ home
+ /jbpm/home.jsp
+
+
+
+
+ /jsp/dashboards/container.jsp
+
+ task
+ /jbpm/task_view.jsp
+
+
+
+
+ /jbpm/task_view.jsp
+
+ home
+ /jsp/dashboards/container.jsp
+
+
+
+
+ /jbpm/monitor.jsp
+
+ processDefinitions
+ /jbpm/process_definitions.jsp
+
+
+ showSearchInstances
+ /jbpm/search_instances.jsp
+
+
+
+
+ /jbpm/process_definitions.jsp
+
+ processInstances
+ /jbpm/process_instances.jsp
+
+
+
+
+ /jbpm/inspect_instance.jsp
+
+ processInstances
+ /jbpm/process_instances.jsp
+
+
+ showTransitions
+ /jbpm/inspect_instance_transitions.jsp
+
+
+
+
+ *
+
+ inspectInstance
+ /jbpm/inspect_instance.jsp
+
+
+
+
diff --git a/source/web/WEB-INF/faces-config-navigation.xml b/source/web/WEB-INF/faces-config-navigation.xml
index cf8683f9f5..44da3c35b6 100644
--- a/source/web/WEB-INF/faces-config-navigation.xml
+++ b/source/web/WEB-INF/faces-config-navigation.xml
@@ -283,6 +283,10 @@
applyTemplate
/jsp/dialog/apply-space-template.jsp
+
+ applyRSSTemplate
+ /jsp/dialog/apply-rss-template.jsp
+
previewSpace
/jsp/dialog/preview-space.jsp
@@ -297,6 +301,14 @@
+
+ /jsp/dialog/apply-rss-template.jsp
+
+ manageInvitedUsers
+ /jsp/roles/manage-invited-users.jsp
+
+
+
/jsp/dialog/document-details.jsp
@@ -619,6 +631,10 @@
changePassword
/jsp/users/change-my-password.jsp
+
+ editUserDetails
+ /jsp/users/edit-user-details.jsp
+
diff --git a/source/web/WEB-INF/faces-config-repo.xml b/source/web/WEB-INF/faces-config-repo.xml
index e4d3bd38ab..9b779c54f9 100644
--- a/source/web/WEB-INF/faces-config-repo.xml
+++ b/source/web/WEB-INF/faces-config-repo.xml
@@ -12,6 +12,11 @@
org.alfresco.faces.Property
org.alfresco.web.ui.repo.component.property.UIProperty
+
+
+
+ org.alfresco.faces.Separator
+ org.alfresco.web.ui.repo.component.property.UISeparator
@@ -129,6 +134,16 @@
org.alfresco.web.ui.repo.component.UINodeInfo
+
+ org.alfresco.faces.DialogButtons
+ org.alfresco.web.ui.repo.component.UIDialogButtons
+
+
+
+ org.alfresco.faces.UserGroupPicker
+ org.alfresco.web.ui.repo.component.UIUserGroupPicker
+
+
@@ -165,6 +180,12 @@
org.alfresco.faces.Property
org.alfresco.faces.PropertyRenderer
org.alfresco.web.ui.repo.renderer.property.PropertyRenderer
+
+
+
+ org.alfresco.faces.Separator
+ org.alfresco.faces.SeparatorRenderer
+ org.alfresco.web.ui.repo.renderer.property.SeparatorRenderer
diff --git a/source/web/WEB-INF/jbpm.tld b/source/web/WEB-INF/jbpm.tld
new file mode 100644
index 0000000000..03dd5ce333
--- /dev/null
+++ b/source/web/WEB-INF/jbpm.tld
@@ -0,0 +1,36 @@
+
+
+
+
+
+ 1.0
+ 1.1
+ jBPM tags
+ jBPM tags
+
+
+
+ processimage
+ org.jbpm.webapp.tag.ProcessImageTag
+ empty
+
+ task
+ true
+ true
+
+
+
+
+ processimageToken
+ org.jbpm.webapp.tag.ProcessImageTag
+ empty
+
+ token
+ true
+ true
+
+
+
+
diff --git a/source/web/WEB-INF/repo.tld b/source/web/WEB-INF/repo.tld
index 02c03607b6..55b9930f2e 100644
--- a/source/web/WEB-INF/repo.tld
+++ b/source/web/WEB-INF/repo.tld
@@ -1538,4 +1538,80 @@
+
+ dialogButtons
+ org.alfresco.web.ui.repo.tag.DialogButtonsTag
+ JSP
+
+
+ The dialogButtons component displays the buttons for a dialog.
+
+
+
+ id
+ false
+ true
+
+
+
+ styleClass
+ false
+ true
+
+
+
+
+ userGroupPicker
+ org.alfresco.web.ui.repo.tag.UserGroupPickerTag
+ JSP
+
+
+ The userGroupPicker component renders a multi-select hierarchical list of groups
+ and users. The groups and be expanded to show the child users and groups for individual
+ selection and deselection.
+
+
+
+ id
+ false
+ true
+
+
+
+ value
+ true
+ true
+
+
+
+ binding
+ false
+ true
+
+
+
+ rendered
+ false
+ true
+
+
+
+ actionListener
+ false
+ true
+
+
+
+ style
+ false
+ true
+
+
+
+ styleClass
+ false
+ true
+
+
+
diff --git a/source/web/WEB-INF/web.xml b/source/web/WEB-INF/web.xml
index 45e03798a7..6ae45b32a1 100644
--- a/source/web/WEB-INF/web.xml
+++ b/source/web/WEB-INF/web.xml
@@ -16,7 +16,7 @@
javax.faces.CONFIG_FILES
- /WEB-INF/faces-config-app.xml,/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-navigation.xml,/WEB-INF/faces-config-common.xml,/WEB-INF/faces-config-repo.xml,WEB-INF/faces-config-custom.xml,/WEB-INF/faces-config-enterprise.xml
+ /WEB-INF/faces-config-app.xml,/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-navigation.xml,/WEB-INF/faces-config-common.xml,/WEB-INF/faces-config-repo.xml,WEB-INF/faces-config-custom.xml,/WEB-INF/faces-config-enterprise.xml,/WEB-INF/faces-config-jbpm.xml
@@ -28,7 +28,7 @@
org.apache.myfaces.DETECT_JAVASCRIPT
false
This is an EXPERIMENTAL feature, so leave it off for now!
-
+
@@ -92,6 +92,31 @@
-->
+
+
+
+
+
+
+ LogFilter
+ org.jbpm.webapp.filter.LogFilter
+
+
+ JbpmContextFilter
+ org.jbpm.webapp.filter.JbpmContextFilter
+
+
+
+
+
+
+
+
Authentication Filter
/faces/*
@@ -111,6 +136,38 @@
/webdav/*
+
+
+
+
+
+
+ LogFilter
+ /faces/jbpm/*
+
+
+ JbpmContextFilter
+ /faces/jbpm/*
+
+
+ JbpmContextFilter
+ /processimage
+
+
+ JbpmContextFilter
+ /faces/jsp/dashboards/*
+
+
+
+
+
+
+
org.apache.myfaces.webapp.StartupServletContextListener
@@ -126,7 +183,7 @@
Faces Servlet
- org.alfresco.web.app.servlet.AlfrescoFacesServlet
+ javax.faces.webapp.FacesServlet
1
@@ -179,7 +236,36 @@
5
-
+
+
+ JBPMDeployProcessServlet
+ org.alfresco.web.app.servlet.JBPMDeployProcessServlet
+
+
+
+
+
+
+
+
+
+
+
+
+ ProcessImageServlet
+ org.jbpm.webapp.servlet.ProcessImageServlet
+
+
+
+
+
+
Faces Servlet
/faces/*
@@ -224,7 +310,34 @@
WebDAV
/webdav/*
+
+
+ JBPMDeployProcessServlet
+ /jbpm/deployprocess
+
+
+
+
+
+
+
+
+
+
+ ProcessImageServlet
+ /processimage
+
+
+
+
+
+
60
@@ -233,4 +346,9 @@
index.jsp
+
+ 500
+ /jsp/error.jsp
+
+
diff --git a/source/web/css/main.css b/source/web/css/main.css
index 7f2e713ba6..dc8bd137bd 100644
--- a/source/web/css/main.css
+++ b/source/web/css/main.css
@@ -346,7 +346,7 @@ input,textarea,select
.topToolbarTitle
{
- background-color:#5D747F;
+ background-color: #465F7D;
color: #ffffff;
font-weight: bold;
padding-left: 5px;
@@ -519,3 +519,12 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
padding: 4px;
-moz-border-radius: 4px;
}
+
+.userGroupPickerList
+{
+ padding: 2px;
+ background-color: #EEEEEE;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #AAAAAA;
+}
diff --git a/source/web/images/icons/add_item.gif b/source/web/images/icons/add_item.gif
new file mode 100644
index 0000000000..977e4ec117
Binary files /dev/null and b/source/web/images/icons/add_item.gif differ
diff --git a/source/web/images/icons/add_item_large.gif b/source/web/images/icons/add_item_large.gif
new file mode 100644
index 0000000000..d67e2b04b3
Binary files /dev/null and b/source/web/images/icons/add_item_large.gif differ
diff --git a/source/web/images/icons/cancel_workflow.gif b/source/web/images/icons/cancel_workflow.gif
new file mode 100644
index 0000000000..8728358b1f
Binary files /dev/null and b/source/web/images/icons/cancel_workflow.gif differ
diff --git a/source/web/images/icons/cancel_workflow_large.gif b/source/web/images/icons/cancel_workflow_large.gif
new file mode 100644
index 0000000000..b8bbfb398e
Binary files /dev/null and b/source/web/images/icons/cancel_workflow_large.gif differ
diff --git a/source/web/images/icons/completed_workflow_item.gif b/source/web/images/icons/completed_workflow_item.gif
new file mode 100644
index 0000000000..65970ff789
Binary files /dev/null and b/source/web/images/icons/completed_workflow_item.gif differ
diff --git a/source/web/images/icons/completed_workflow_item_large.gif b/source/web/images/icons/completed_workflow_item_large.gif
new file mode 100644
index 0000000000..d3014a8d8c
Binary files /dev/null and b/source/web/images/icons/completed_workflow_item_large.gif differ
diff --git a/source/web/images/icons/configure_dashboard.gif b/source/web/images/icons/configure_dashboard.gif
index d79e1a233b..56c1a7f64b 100644
Binary files a/source/web/images/icons/configure_dashboard.gif and b/source/web/images/icons/configure_dashboard.gif differ
diff --git a/source/web/images/icons/configure_dashboard_large.gif b/source/web/images/icons/configure_dashboard_large.gif
index 336f7967dd..9656c03235 100644
Binary files a/source/web/images/icons/configure_dashboard_large.gif and b/source/web/images/icons/configure_dashboard_large.gif differ
diff --git a/source/web/images/icons/dashboard.gif b/source/web/images/icons/dashboard.gif
index d79e1a233b..bbaef9e2fc 100644
Binary files a/source/web/images/icons/dashboard.gif and b/source/web/images/icons/dashboard.gif differ
diff --git a/source/web/images/icons/dashboard_large.gif b/source/web/images/icons/dashboard_large.gif
index 336f7967dd..cf542588bd 100644
Binary files a/source/web/images/icons/dashboard_large.gif and b/source/web/images/icons/dashboard_large.gif differ
diff --git a/source/web/images/icons/email_users.gif b/source/web/images/icons/email_users.gif
new file mode 100644
index 0000000000..31ddcd03a0
Binary files /dev/null and b/source/web/images/icons/email_users.gif differ
diff --git a/source/web/images/icons/email_users_large.gif b/source/web/images/icons/email_users_large.gif
new file mode 100644
index 0000000000..ac879afcdb
Binary files /dev/null and b/source/web/images/icons/email_users_large.gif differ
diff --git a/source/web/images/icons/manage_workflow_item_large.gif b/source/web/images/icons/manage_workflow_item_large.gif
new file mode 100644
index 0000000000..442c772fb6
Binary files /dev/null and b/source/web/images/icons/manage_workflow_item_large.gif differ
diff --git a/source/web/images/icons/new_workflow.gif b/source/web/images/icons/new_workflow.gif
new file mode 100644
index 0000000000..d37fe0bc94
Binary files /dev/null and b/source/web/images/icons/new_workflow.gif differ
diff --git a/source/web/images/icons/new_workflow_large.gif b/source/web/images/icons/new_workflow_large.gif
new file mode 100644
index 0000000000..9c7e869856
Binary files /dev/null and b/source/web/images/icons/new_workflow_large.gif differ
diff --git a/source/web/images/icons/reassign_workflow_item.gif b/source/web/images/icons/reassign_workflow_item.gif
new file mode 100644
index 0000000000..95f2962dc8
Binary files /dev/null and b/source/web/images/icons/reassign_workflow_item.gif differ
diff --git a/source/web/images/icons/reassign_workflow_item_large.gif b/source/web/images/icons/reassign_workflow_item_large.gif
new file mode 100644
index 0000000000..09c30fb95e
Binary files /dev/null and b/source/web/images/icons/reassign_workflow_item_large.gif differ
diff --git a/source/web/images/icons/remove_item.gif b/source/web/images/icons/remove_item.gif
new file mode 100644
index 0000000000..b1b114165f
Binary files /dev/null and b/source/web/images/icons/remove_item.gif differ
diff --git a/source/web/images/icons/remove_item_large.gif b/source/web/images/icons/remove_item_large.gif
new file mode 100644
index 0000000000..8417c4405b
Binary files /dev/null and b/source/web/images/icons/remove_item_large.gif differ
diff --git a/source/web/images/icons/rss.gif b/source/web/images/icons/rss.gif
new file mode 100644
index 0000000000..0e9be56673
Binary files /dev/null and b/source/web/images/icons/rss.gif differ
diff --git a/source/web/images/icons/rss_large.gif b/source/web/images/icons/rss_large.gif
new file mode 100644
index 0000000000..5b3e2273d7
Binary files /dev/null and b/source/web/images/icons/rss_large.gif differ
diff --git a/source/web/images/icons/user_console.gif b/source/web/images/icons/user_console.gif
index 39a7969d3c..1537eab355 100644
Binary files a/source/web/images/icons/user_console.gif and b/source/web/images/icons/user_console.gif differ
diff --git a/source/web/images/icons/user_console_large.gif b/source/web/images/icons/user_console_large.gif
index 0f0cd28945..50a9a1cf3c 100644
Binary files a/source/web/images/icons/user_console_large.gif and b/source/web/images/icons/user_console_large.gif differ
diff --git a/source/web/images/icons/workflow.gif b/source/web/images/icons/workflow.gif
new file mode 100644
index 0000000000..000df7c04c
Binary files /dev/null and b/source/web/images/icons/workflow.gif differ
diff --git a/source/web/images/icons/workflow_item.gif b/source/web/images/icons/workflow_item.gif
new file mode 100644
index 0000000000..6cee9ec039
Binary files /dev/null and b/source/web/images/icons/workflow_item.gif differ
diff --git a/source/web/images/icons/workflow_item_large.gif b/source/web/images/icons/workflow_item_large.gif
new file mode 100644
index 0000000000..bf5bd91084
Binary files /dev/null and b/source/web/images/icons/workflow_item_large.gif differ
diff --git a/source/web/images/icons/workflow_large.gif b/source/web/images/icons/workflow_large.gif
new file mode 100644
index 0000000000..183d71c2ca
Binary files /dev/null and b/source/web/images/icons/workflow_large.gif differ
diff --git a/source/web/jbpm/admin.jsp b/source/web/jbpm/admin.jsp
new file mode 100644
index 0000000000..5f614a7d8e
--- /dev/null
+++ b/source/web/jbpm/admin.jsp
@@ -0,0 +1,47 @@
+<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
+<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
+
+
+
+Administration
+
+
+
+
+TODO
+
+<%--
+(this page is not yet implemented)
+
+Deploy process
+
+
+
+
+
+Database Schema
+
+
+
+
+
+
+
+Scheduler
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+--%>
+
+
diff --git a/source/web/jbpm/css/jbpm.css b/source/web/jbpm/css/jbpm.css
new file mode 100644
index 0000000000..f835ebc084
--- /dev/null
+++ b/source/web/jbpm/css/jbpm.css
@@ -0,0 +1,106 @@
+body, td, p {
+ font-family:verdana;
+ font-size:10pt;
+}
+
+a {
+ color: rgb(110, 110, 110);
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+div.nav2 {
+ background-color:#5c5c4f;
+ color:#ffffff;
+ font-size: 12px;
+ font-weight: bold;
+ text-decoration: none;
+ line-height: 12px;
+ border: 0px;
+ margin-left: 0px;
+ margin-right: 1px;
+ margin-top: 1px;
+ margin-bottom: 0px;
+ padding-top:5px;
+ padding-bottom:5px;
+ width:174px;
+ cursor: pointer;
+}
+
+div.innerNav {
+ position:relative;
+ left:10px;
+}
+
+a.nav {
+ font-size: 12px;
+ font-weight: bold;
+ text-decoration: none;
+ line-height: 12px;
+ width:100%;
+ background-color:#5c5c4f;
+ color:#ffffff;
+ border: 0px;
+ margin-left: 0px;
+ margin-right: 1px;
+ margin-top: 1px;
+ margin-bottom: 0px;
+ padding-left:10px;
+ padding-right:10px;
+ padding-top:5px;
+ padding-bottom:5px;
+}
+
+div.nav {
+ font-size: 12px;
+ font-weight: bold;
+ text-decoration: none;
+ line-height: 12px;
+ width:174;
+ background-color:#5c5c4f;
+ color:#ffffff;
+ border: 0px;
+ height: 20px;
+ margin-left: 0px;
+ margin-right: 1px;
+ margin-top: 1px;
+ margin-bottom: 0px;
+ vertical-align:middle;
+ padding-left:10px;
+ padding-top:5px;
+ padding-bottom:5px;
+}
+
+a.ref {
+ padding: 5px;
+ color: rgb(110, 110, 110);
+ text-decoration: none;
+ font-size: 11px;
+ line-height: 18px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family:arial;
+ color:purple;
+ border-bottom:0px;
+ margin-bottom:0px;
+ padding-bottom:0px;
+}
+
+td.tablecell {
+ padding-left:10px;
+ padding-right:10px;
+ background-color:#eeeeee;
+}
+
+th.tableheader {
+ text-align:center;
+ text-weight:bold;
+ padding-right:10px;
+ padding-left:10px;
+ color:#ffffff;
+ background-color:#999999;
+}
diff --git a/source/web/jbpm/footer.jsp b/source/web/jbpm/footer.jsp
new file mode 100644
index 0000000000..91b78efbf3
--- /dev/null
+++ b/source/web/jbpm/footer.jsp
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+