mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merge V1.4 to HEAD
- Ignored Enterprise-specific changes svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3701 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3703 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3704 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3705 . svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3707 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@3876 . svn revert root\projects\web-client\source\web\jsp\admin\admin-console.jsp git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3879 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -996,10 +996,22 @@ reassign_select_user=Select the user to assign the task to, then press OK.
|
||||
error_reassign_task=Unable to reassign the task due to system error:
|
||||
part_of_workflow=Part of Workflow
|
||||
initiated_by=Initiated by
|
||||
start_date=Start date
|
||||
started_on=Started on
|
||||
add_resource=Add Resource
|
||||
view_properties=View Content Properties
|
||||
edit_properties=Edit Content Properties
|
||||
save_changes=Save Changes
|
||||
no_tasks=No tasks found.
|
||||
no_resources=No resources found.
|
||||
in_progress=In Progress
|
||||
|
||||
# Workflow Definitions
|
||||
wf_review_due_date=Review Due Date
|
||||
wf_review_priority=Review Priority
|
||||
wf_reviewer=Reviewer
|
||||
wf_adhoc_due_date=Due Date
|
||||
wf_adhoc_priority=Priority
|
||||
wf_adhoc_assignee=Assign To
|
||||
|
||||
# Admin Console messages
|
||||
title_admin_console=Administration Console
|
||||
|
@@ -230,9 +230,21 @@
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="bpm:taskId" />
|
||||
<show-property name="bpm:description" component-generator="TextAreaGenerator" read-only="true"/>
|
||||
<show-property name="bpm:dueDate" read-only="true" />
|
||||
<show-property name="bpm:priority" read-only="true" />
|
||||
<show-property name="bpm:status" />
|
||||
<show-property name="bpm:dueDate" />
|
||||
<show-property name="bpm:priority" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
<config evaluator="node-type" condition="bpm:startTask" replace="true">
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="bpm:workflowDescription" component-generator="TextAreaGenerator" />
|
||||
<show-property name="bpm:workflowPriority" />
|
||||
<show-property name="bpm:workflowDueDate" />
|
||||
<separator name="sep2" display-label-id="users_and_roles" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-association name="bpm:assignee" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
@@ -242,19 +254,21 @@
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="bpm:taskId" />
|
||||
<show-property name="bpm:description" component-generator="TextAreaGenerator" read-only="true"/>
|
||||
<show-property name="bpm:dueDate" read-only="true" />
|
||||
<show-property name="bpm:priority" read-only="true" />
|
||||
<show-property name="bpm:status" />
|
||||
<show-property name="bpm:dueDate" />
|
||||
<show-property name="bpm:priority" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
<config evaluator="node-type" condition="wf:submitReviewTask" replace="true">
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="wf:reviewPriority" />
|
||||
<show-property name="wf:reviewDueDate" />
|
||||
<show-property name="bpm:workflowDescription" component-generator="TextAreaGenerator" />
|
||||
<show-property name="bpm:workflowPriority" display-label-id="wf_review_priority" />
|
||||
<show-property name="bpm:workflowDueDate" display-label-id="wf_review_due_date" />
|
||||
<separator name="sep2" display-label-id="users_and_roles" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-association name="wf:reviewer" />
|
||||
<show-association name="bpm:assignee" display-label-id="wf_reviewer" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
@@ -263,12 +277,12 @@
|
||||
<config evaluator="node-type" condition="wf:submitAdhocTask" replace="true">
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="wf:adhocDescription" component-generator="TextAreaGenerator" />
|
||||
<show-property name="wf:adhocPriority" />
|
||||
<show-property name="wf:adhocDueDate" />
|
||||
<show-property name="bpm:workflowDescription" component-generator="TextAreaGenerator" />
|
||||
<show-property name="bpm:workflowPriority" display-label-id="wf_adhoc_priority" />
|
||||
<show-property name="bpm:workflowDueDate" display-label-id="wf_adhoc_due_date" />
|
||||
<show-property name="wf:notifyMe" />
|
||||
<separator name="sep2" display-label-id="users_and_roles" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-association name="wf:assignee" />
|
||||
<separator name="sep2" display-label-id="user_filter_user" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-association name="bpm:assignee" display-label-id="wf_adhoc_assignee" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
@@ -276,10 +290,10 @@
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="bpm:taskId" />
|
||||
<show-property name="wf:adhocDescription" ignore-if-missing="false" component-generator="TextAreaGenerator" />
|
||||
<show-property name="bpm:description" component-generator="TextAreaGenerator" read-only="true" />
|
||||
<show-property name="bpm:dueDate" read-only="true" />
|
||||
<show-property name="bpm:priority" read-only="true" />
|
||||
<show-property name="bpm:status" />
|
||||
<show-property name="bpm:dueDate" />
|
||||
<show-property name="bpm:priority" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
@@ -287,9 +301,9 @@
|
||||
<property-sheet>
|
||||
<separator name="sep1" display-label-id="general" component-generator="HeaderSeparatorGenerator" />
|
||||
<show-property name="bpm:taskId" />
|
||||
<show-property name="wf:adhocDescription" component-generator="TextAreaGenerator" />
|
||||
<show-property name="bpm:dueDate" />
|
||||
<show-property name="bpm:priority" />
|
||||
<show-property name="bpm:description" component-generator="TextAreaGenerator" read-only="true" />
|
||||
<show-property name="bpm:dueDate" read-only="true" />
|
||||
<show-property name="bpm:priority" read-only="true" />
|
||||
</property-sheet>
|
||||
</config>
|
||||
|
||||
|
@@ -13,19 +13,40 @@
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<action id="manage_task">
|
||||
<label-id>manage_task</label-id>
|
||||
<image>/images/icons/manage_workflow_task.gif</image>
|
||||
<action>dialog:manageTask</action>
|
||||
<action-listener>#{DialogManager.setupParameters}</action-listener>
|
||||
<params>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<action id="view_completed_task">
|
||||
<label-id>view_completed_task_title</label-id>
|
||||
<image>/images/icons/view_workflow_task.gif</image>
|
||||
<action>dialog:viewCompletedTask</action>
|
||||
<action-listener>#{DialogManager.setupParameters}</action-listener>
|
||||
<params>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<action id="reassign_task">
|
||||
<label-id>reassign</label-id>
|
||||
<image>/images/icons/reassign_task.gif</image>
|
||||
<action>dialog:reassignTask</action>
|
||||
<action-listener>#{DialogManager.setupParameters}</action-listener>
|
||||
<params>
|
||||
<param name="task-id">#{actionContext.id}</param>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<action id="cancel_workflow">
|
||||
<label-id>cancel_workflow</label-id>
|
||||
<image>/images/icons/cancel_workflow.gif</image>
|
||||
<evaluator>org.alfresco.web.action.evaluator.CancelWorkflowEvaluator</evaluator>
|
||||
<action>dialog:cancelWorkflow</action>
|
||||
<action-listener>#{DialogManager.setupParameters}</action-listener>
|
||||
<params>
|
||||
@@ -51,7 +72,7 @@
|
||||
|
||||
<action id="view_content_properties">
|
||||
<label-id>view_properties</label-id>
|
||||
<image>/images/icons/View_details.gif</image>
|
||||
<image>/images/icons/view_properties.gif</image>
|
||||
<action>dialog:viewContentProperties</action>
|
||||
<action-listener>#{BrowseBean.setupContentAction}</action-listener>
|
||||
<params>
|
||||
@@ -64,7 +85,7 @@
|
||||
<permission allow="true">Write</permission>
|
||||
</permissions>
|
||||
<label-id>edit_properties</label-id>
|
||||
<image>/images/icons/Change_details.gif</image>
|
||||
<image>/images/icons/edit_properties.gif</image>
|
||||
<action>dialog:editContentProperties</action>
|
||||
<action-listener>#{BrowseBean.setupContentAction}</action-listener>
|
||||
<params>
|
||||
@@ -72,6 +93,45 @@
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<!-- Checkin document from workflow package -->
|
||||
<action id="workflow_checkin_doc">
|
||||
<evaluator>org.alfresco.web.action.evaluator.CheckinDocEvaluator</evaluator>
|
||||
<label-id>checkin</label-id>
|
||||
<image>/images/icons/CheckIn_icon.gif</image>
|
||||
<action-listener>#{CheckinCheckoutBean.setupWorkflowContentAction}</action-listener>
|
||||
<action>dialog:checkinFile</action>
|
||||
<params>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
<param name="taskId">#{actionContext.taskId}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<!-- Checkout document from workflow package -->
|
||||
<action id="workflow_checkout_doc">
|
||||
<evaluator>org.alfresco.web.action.evaluator.CheckoutDocEvaluator</evaluator>
|
||||
<label-id>checkout</label-id>
|
||||
<image>/images/icons/CheckOut_icon.gif</image>
|
||||
<action-listener>#{CheckinCheckoutBean.setupWorkflowContentAction}</action-listener>
|
||||
<action>dialog:checkoutFile</action>
|
||||
<params>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
<param name="taskId">#{actionContext.taskId}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<!-- Cancel checkout document from workflow package -->
|
||||
<action id="workflow_cancelcheckout_doc">
|
||||
<evaluator>org.alfresco.web.action.evaluator.CancelCheckoutDocEvaluator</evaluator>
|
||||
<label-id>undocheckout</label-id>
|
||||
<image>/images/icons/undo_checkout.gif</image>
|
||||
<action-listener>#{CheckinCheckoutBean.setupWorkflowContentAction}</action-listener>
|
||||
<action>dialog:undoCheckoutFile</action>
|
||||
<params>
|
||||
<param name="id">#{actionContext.id}</param>
|
||||
<param name="taskId">#{actionContext.taskId}</param>
|
||||
</params>
|
||||
</action>
|
||||
|
||||
<action-group id="document_browse_menu">
|
||||
<action idref="start_workflow" />
|
||||
</action-group>
|
||||
@@ -81,10 +141,12 @@
|
||||
</action-group>
|
||||
|
||||
<action-group id="dashlet_todo_actions">
|
||||
<action idref="manage_task" />
|
||||
<action idref="reassign_task" />
|
||||
</action-group>
|
||||
|
||||
<action-group id="dashlet_completed_actions">
|
||||
<action idref="view_completed_task" />
|
||||
<action idref="cancel_workflow" />
|
||||
</action-group>
|
||||
|
||||
@@ -104,9 +166,9 @@
|
||||
<action idref="edit_doc_webdav" />
|
||||
<action idref="edit_doc_cifs" />
|
||||
<action idref="update_doc" />
|
||||
<action idref="checkout_doc" />
|
||||
<action idref="checkin_doc" />
|
||||
<action idref="cancelcheckout_doc" />
|
||||
<action idref="workflow_checkout_doc" />
|
||||
<action idref="workflow_checkin_doc" />
|
||||
<action idref="workflow_cancelcheckout_doc" />
|
||||
</action-group>
|
||||
|
||||
<action-group id="workflow_collection_actions">
|
||||
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.action.evaluator;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
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.util.ISO9075;
|
||||
import org.alfresco.web.action.ActionEvaluator;
|
||||
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.User;
|
||||
|
||||
/**
|
||||
* UI Action Evaluator for cancel workflow action. The action
|
||||
* is only allowed if the workflow the task belongs to was
|
||||
* started by the current user.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class CancelWorkflowEvaluator implements ActionEvaluator
|
||||
{
|
||||
/**
|
||||
* @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node)
|
||||
*/
|
||||
public boolean evaluate(Node node)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
// get the id of the task
|
||||
String taskId = (String)node.getProperties().get("id");
|
||||
if (taskId != null)
|
||||
{
|
||||
FacesContext context = FacesContext.getCurrentInstance();
|
||||
|
||||
// get the initiator of the workflow the task belongs to
|
||||
WorkflowService workflowSvc = Repository.getServiceRegistry(
|
||||
context).getWorkflowService();
|
||||
|
||||
WorkflowTask task = workflowSvc.getTaskById(taskId);
|
||||
if (task != null)
|
||||
{
|
||||
NodeRef initiator = task.path.instance.initiator;
|
||||
if (initiator != null)
|
||||
{
|
||||
// find the current username
|
||||
User user = Application.getCurrentUser(context);
|
||||
String currentUserName = ISO9075.encode(user.getUserName());
|
||||
|
||||
// get the username of the initiator
|
||||
NodeService nodeSvc = Repository.getServiceRegistry(
|
||||
context).getNodeService();
|
||||
String userName = (String)nodeSvc.getProperty(initiator, ContentModel.PROP_USERNAME);
|
||||
|
||||
// if the current user started the workflow allow the cancel action
|
||||
if (currentUserName.equals(userName))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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.HttpServletResponse;
|
||||
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* This servlet filter is used to restrict direct URL access to administration
|
||||
* resource in the web client, for example the admin and jBPM consoles.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class AdminAuthenticationFilter implements Filter
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(AdminAuthenticationFilter.class);
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
||||
*/
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
HttpServletRequest httpRequest = (HttpServletRequest)req;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse)res;
|
||||
|
||||
// The fact that this filter is being called means a request for a protected
|
||||
// resource has taken place, check that the current user is in fact an
|
||||
// administrator.
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Authorising request for protected resource: " + httpRequest.getRequestURI());
|
||||
|
||||
// there should be a user at this point so retrieve it
|
||||
User user = AuthenticationHelper.getUser(httpRequest, httpResponse);
|
||||
|
||||
// if the user is present check to see whether it is an admin user
|
||||
boolean isAdmin = (user != null && user.isAdmin());
|
||||
|
||||
if (isAdmin)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Current user has admin authority, allowing access.");
|
||||
|
||||
// continue filter chaining if current user is admin user
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// return the 401 Forbidden error as the current user is not an administrator
|
||||
// if the response has already been committed there's nothing we can do but
|
||||
// print out a warning
|
||||
if (httpResponse.isCommitted() == false)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Current user does not have admin authority, returning 401 Forbidden error...");
|
||||
|
||||
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isWarnEnabled())
|
||||
logger.warn("Access denied to '" + httpRequest.getRequestURI() +
|
||||
"'. The response has already been committed so a 401 Forbidden error could not be sent!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
|
||||
*/
|
||||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#destroy()
|
||||
*/
|
||||
public void destroy()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
}
|
@@ -100,39 +100,15 @@ public final class AuthenticationHelper
|
||||
{
|
||||
HttpSession session = httpRequest.getSession();
|
||||
|
||||
// examine the appropriate session for our User object
|
||||
User user = null;
|
||||
// retrieve the User object
|
||||
User user = getUser(httpRequest, httpResponse);
|
||||
|
||||
// get the login bean if we're not in the portal
|
||||
LoginBean loginBean = null;
|
||||
if (Application.inPortalServer() == false)
|
||||
{
|
||||
user = (User)session.getAttribute(AUTHENTICATION_USER);
|
||||
loginBean = (LoginBean)session.getAttribute(LOGIN_BEAN);
|
||||
}
|
||||
else
|
||||
{
|
||||
// naff solution as we need to enumerate all session keys until we find the one that
|
||||
// should match our User objects - this is weak but we don't know how the underlying
|
||||
// Portal vendor has decided to encode the objects in the session
|
||||
if (portalUserKeyName.get() == null)
|
||||
{
|
||||
String userKeyPostfix = "?" + AUTHENTICATION_USER;
|
||||
Enumeration enumNames = session.getAttributeNames();
|
||||
while (enumNames.hasMoreElements())
|
||||
{
|
||||
String name = (String)enumNames.nextElement();
|
||||
if (name.endsWith(userKeyPostfix))
|
||||
{
|
||||
// cache the key value once found!
|
||||
portalUserKeyName.set(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (portalUserKeyName.get() != null)
|
||||
{
|
||||
user = (User)session.getAttribute(portalUserKeyName.get());
|
||||
}
|
||||
}
|
||||
|
||||
// setup the authentication context
|
||||
WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
|
||||
@@ -388,6 +364,52 @@ public final class AuthenticationHelper
|
||||
return AuthenticationStatus.Failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve the User object stored in the current session.
|
||||
*
|
||||
* @param httpRequest The HTTP request
|
||||
* @param httpResponse The HTTP response
|
||||
* @return The User object representing the current user or null if it could not be found
|
||||
*/
|
||||
public static User getUser(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
|
||||
{
|
||||
HttpSession session = httpRequest.getSession();
|
||||
User user = null;
|
||||
|
||||
// examine the appropriate session to try and find the User object
|
||||
if (Application.inPortalServer() == false)
|
||||
{
|
||||
user = (User)session.getAttribute(AUTHENTICATION_USER);
|
||||
}
|
||||
else
|
||||
{
|
||||
// naff solution as we need to enumerate all session keys until we find the one that
|
||||
// should match our User objects - this is weak but we don't know how the underlying
|
||||
// Portal vendor has decided to encode the objects in the session
|
||||
if (portalUserKeyName.get() == null)
|
||||
{
|
||||
String userKeyPostfix = "?" + AUTHENTICATION_USER;
|
||||
Enumeration enumNames = session.getAttributeNames();
|
||||
while (enumNames.hasMoreElements())
|
||||
{
|
||||
String name = (String)enumNames.nextElement();
|
||||
if (name.endsWith(userKeyPostfix))
|
||||
{
|
||||
// cache the key value once found!
|
||||
portalUserKeyName.set(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (portalUserKeyName.get() != null)
|
||||
{
|
||||
user = (User)session.getAttribute(portalUserKeyName.get());
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the Alfresco auth cookie value.
|
||||
*
|
||||
|
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.SocketException;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
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.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
/**
|
||||
* Base class for the download content servlets. Provides common
|
||||
* processing for the request.
|
||||
*
|
||||
* @see org.alfresco.web.app.servlet.DownloadContentServlet
|
||||
* @see org.alfresco.web.app.servlet.GuestDownloadContentServlet
|
||||
*
|
||||
* @author Kevin Roast
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class BaseDownloadContentServlet extends BaseServlet
|
||||
{
|
||||
private static final long serialVersionUID = -4558907921887235966L;
|
||||
|
||||
protected static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
|
||||
|
||||
protected static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
|
||||
|
||||
protected static final String ARG_PROPERTY = "property";
|
||||
protected static final String ARG_ATTACH = "attach";
|
||||
protected static final String ARG_PATH = "path";
|
||||
|
||||
/**
|
||||
* Gets the logger to use for this request.
|
||||
* <p>
|
||||
* This will show all debug entries from this class as though they
|
||||
* came from the subclass.
|
||||
*
|
||||
* @return The logger
|
||||
*/
|
||||
protected abstract Log getLogger();
|
||||
|
||||
/**
|
||||
* Processes the download request using the current context i.e. no
|
||||
* authentication checks are made, it is presumed they have already
|
||||
* been done.
|
||||
*
|
||||
* @param req The HTTP request
|
||||
* @param res The HTTP response
|
||||
* @param redirectToLogin Flag to determine whether to redirect to the login
|
||||
* page if the user does not have the correct permissions
|
||||
*/
|
||||
protected void processDownloadRequest(HttpServletRequest req, HttpServletResponse res,
|
||||
boolean redirectToLogin)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
Log logger = getLogger();
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
String queryString = req.getQueryString();
|
||||
logger.debug("Processing URL: " + uri +
|
||||
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
|
||||
}
|
||||
|
||||
// TODO: add compression here?
|
||||
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
|
||||
// only really needed if we don't use the built in compression of the servlet container
|
||||
uri = uri.substring(req.getContextPath().length());
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
int tokenCount = t.countTokens();
|
||||
|
||||
t.nextToken(); // skip servlet name
|
||||
|
||||
// attachment mode (either 'attach' or 'direct')
|
||||
String attachToken = t.nextToken();
|
||||
boolean attachment = attachToken.equals(ARG_ATTACH);
|
||||
|
||||
// get or calculate the noderef and filename to download as
|
||||
NodeRef nodeRef;
|
||||
String filename;
|
||||
|
||||
// do we have a path parameter instead of a NodeRef?
|
||||
String path = req.getParameter(ARG_PATH);
|
||||
if (path != null && path.length() != 0)
|
||||
{
|
||||
// process the name based path to resolve the NodeRef and the Filename element
|
||||
PathRefInfo pathInfo = resolveNamePath(getServletContext(), path);
|
||||
|
||||
nodeRef = pathInfo.NodeRef;
|
||||
filename = pathInfo.Filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
// a NodeRef must have been specified if no path has been found
|
||||
if (tokenCount < 6)
|
||||
{
|
||||
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
|
||||
}
|
||||
|
||||
// assume 'workspace' or other NodeRef based protocol for remaining URL elements
|
||||
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
|
||||
String id = t.nextToken();
|
||||
// build noderef from the appropriate URL elements
|
||||
nodeRef = new NodeRef(storeRef, id);
|
||||
|
||||
// filename is last remaining token
|
||||
filename = t.nextToken();
|
||||
}
|
||||
|
||||
// get qualified of the property to get content from - default to ContentModel.PROP_CONTENT
|
||||
QName propertyQName = ContentModel.PROP_CONTENT;
|
||||
String property = req.getParameter(ARG_PROPERTY);
|
||||
if (property != null && property.length() != 0)
|
||||
{
|
||||
propertyQName = QName.createQName(property);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Found NodeRef: " + nodeRef.toString());
|
||||
logger.debug("Will use filename: " + filename);
|
||||
logger.debug("For property: " + propertyQName);
|
||||
logger.debug("With attachment mode: " + attachment);
|
||||
}
|
||||
|
||||
// get the services we need to retrieve the content
|
||||
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
|
||||
NodeService nodeService = serviceRegistry.getNodeService();
|
||||
ContentService contentService = serviceRegistry.getContentService();
|
||||
PermissionService permissionService = serviceRegistry.getPermissionService();
|
||||
|
||||
try
|
||||
{
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("User does not have permissions to read content for NodeRef: " + nodeRef.toString());
|
||||
|
||||
if (redirectToLogin)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Redirecting to login page...");
|
||||
|
||||
redirectToLoginPage(req, res, getServletContext());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Returning 403 Forbidden error...");
|
||||
|
||||
res.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
long modifiedSince = req.getDateHeader("If-Modified-Since");
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
res.setStatus(304);
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.setDateHeader("Last-Modified", modified.getTime());
|
||||
|
||||
if (attachment == true)
|
||||
{
|
||||
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
|
||||
// this is better than the default response of the browser trying to display the contents
|
||||
res.setHeader("Content-Disposition", "attachment");
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
// ensure that it is safe to use
|
||||
reader = FileContentReader.getSafeContentReader(
|
||||
reader,
|
||||
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
|
||||
nodeRef, reader);
|
||||
|
||||
String mimetype = reader.getMimetype();
|
||||
// fall back if unable to resolve mimetype property
|
||||
if (mimetype == null || mimetype.length() == 0)
|
||||
{
|
||||
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
|
||||
mimetype = MIMETYPE_OCTET_STREAM;
|
||||
int extIndex = filename.lastIndexOf('.');
|
||||
if (extIndex != -1)
|
||||
{
|
||||
String ext = filename.substring(extIndex + 1);
|
||||
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
|
||||
if (mt != null)
|
||||
{
|
||||
mimetype = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
// set mimetype for the content and the character encoding for the stream
|
||||
res.setContentType(mimetype);
|
||||
res.setCharacterEncoding(reader.getEncoding());
|
||||
|
||||
// get the content and stream directly to the response output stream
|
||||
// assuming the repo is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
reader.getContent( res.getOutputStream() );
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.getMessage().contains("ClientAbortException"))
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
*
|
||||
* @param pattern The pattern to use for the URL
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
protected final static String generateUrl(String pattern, NodeRef ref, String name)
|
||||
{
|
||||
String url = null;
|
||||
|
||||
try
|
||||
{
|
||||
url = MessageFormat.format(pattern, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
@@ -73,7 +73,7 @@ public class CommandServlet extends BaseServlet
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
protected void service(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String uri = req.getRequestURI();
|
||||
|
@@ -17,32 +17,12 @@
|
||||
package org.alfresco.web.app.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.SocketException;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
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.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
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;
|
||||
|
||||
@@ -71,26 +51,30 @@ import org.apache.commons.logging.LogFactory;
|
||||
* Like most Alfresco servlets, the URL may be followed by a valid 'ticket' argument for authentication:
|
||||
* ?ticket=1234567890
|
||||
* <p>
|
||||
* And/or also followed by the "?guest=true" argument to force guest access login for the URL.
|
||||
* And/or also followed by the "?guest=true" argument to force guest access login for the URL. If the
|
||||
* guest=true parameter is used the current session will be logged out and the guest user logged in.
|
||||
* Therefore upon completion of this request the current user will be "guest".
|
||||
* <p>
|
||||
* If the user attempting the request is not authorised to access the requested node the login page
|
||||
* will be redirected to.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
* @author gavinc
|
||||
*/
|
||||
public class DownloadContentServlet extends BaseServlet
|
||||
public class DownloadContentServlet extends BaseDownloadContentServlet
|
||||
{
|
||||
private static final long serialVersionUID = -4558907921887235966L;
|
||||
|
||||
private static final long serialVersionUID = -576405943603122206L;
|
||||
|
||||
private static Log logger = LogFactory.getLog(DownloadContentServlet.class);
|
||||
|
||||
private static final String DOWNLOAD_URL = "/download/attach/{0}/{1}/{2}/{3}";
|
||||
private static final String BROWSER_URL = "/download/direct/{0}/{1}/{2}/{3}";
|
||||
|
||||
private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
|
||||
|
||||
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
|
||||
|
||||
private static final String ARG_PROPERTY = "property";
|
||||
private static final String ARG_ATTACH = "attach";
|
||||
private static final String ARG_PATH = "path";
|
||||
@Override
|
||||
protected Log getLogger()
|
||||
{
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
@@ -98,10 +82,12 @@ public class DownloadContentServlet extends BaseServlet
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
|
||||
{
|
||||
String queryString = req.getQueryString();
|
||||
logger.debug("Authenticating request to URL: " + req.getRequestURI() +
|
||||
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
|
||||
}
|
||||
|
||||
AuthenticationStatus status = servletAuthenticate(req, res);
|
||||
if (status == AuthenticationStatus.Failure)
|
||||
@@ -109,159 +95,7 @@ public class DownloadContentServlet extends BaseServlet
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: add compression here?
|
||||
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
|
||||
// only really needed if we don't use the built in compression of the servlet container
|
||||
uri = uri.substring(req.getContextPath().length());
|
||||
StringTokenizer t = new StringTokenizer(uri, "/");
|
||||
int tokenCount = t.countTokens();
|
||||
|
||||
t.nextToken(); // skip servlet name
|
||||
|
||||
// attachment mode (either 'attach' or 'direct')
|
||||
String attachToken = t.nextToken();
|
||||
boolean attachment = attachToken.equals(ARG_ATTACH);
|
||||
|
||||
// get or calculate the noderef and filename to download as
|
||||
NodeRef nodeRef;
|
||||
String filename;
|
||||
|
||||
// do we have a path parameter instead of a NodeRef?
|
||||
String path = req.getParameter(ARG_PATH);
|
||||
if (path != null && path.length() != 0)
|
||||
{
|
||||
// process the name based path to resolve the NodeRef and the Filename element
|
||||
PathRefInfo pathInfo = resolveNamePath(getServletContext(), path);
|
||||
|
||||
nodeRef = pathInfo.NodeRef;
|
||||
filename = pathInfo.Filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
// a NodeRef must have been specified if no path has been found
|
||||
if (tokenCount < 6)
|
||||
{
|
||||
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
|
||||
}
|
||||
|
||||
// assume 'workspace' or other NodeRef based protocol for remaining URL elements
|
||||
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
|
||||
String id = t.nextToken();
|
||||
// build noderef from the appropriate URL elements
|
||||
nodeRef = new NodeRef(storeRef, id);
|
||||
|
||||
// filename is last remaining token
|
||||
filename = t.nextToken();
|
||||
}
|
||||
|
||||
// get qualified of the property to get content from - default to ContentModel.PROP_CONTENT
|
||||
QName propertyQName = ContentModel.PROP_CONTENT;
|
||||
String property = req.getParameter(ARG_PROPERTY);
|
||||
if (property != null && property.length() != 0)
|
||||
{
|
||||
propertyQName = QName.createQName(property);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Found NodeRef: " + nodeRef.toString());
|
||||
logger.debug("Will use filename: " + filename);
|
||||
logger.debug("For property: " + propertyQName);
|
||||
logger.debug("With attachment mode: " + attachment);
|
||||
}
|
||||
|
||||
// get the services we need to retrieve the content
|
||||
ServiceRegistry serviceRegistry = getServiceRegistry(getServletContext());
|
||||
NodeService nodeService = serviceRegistry.getNodeService();
|
||||
ContentService contentService = serviceRegistry.getContentService();
|
||||
PermissionService permissionService = serviceRegistry.getPermissionService();
|
||||
|
||||
try
|
||||
{
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("User does not have permissions to read content for NodeRef: " + nodeRef.toString());
|
||||
redirectToLoginPage(req, res, getServletContext());
|
||||
return;
|
||||
}
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
long modifiedSince = req.getDateHeader("If-Modified-Since");
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
res.setStatus(304);
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.setDateHeader("Last-Modified", modified.getTime());
|
||||
|
||||
if (attachment == true)
|
||||
{
|
||||
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
|
||||
// this is better than the default response of the browser trying to display the contents
|
||||
res.setHeader("Content-Disposition", "attachment");
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
// ensure that it is safe to use
|
||||
reader = FileContentReader.getSafeContentReader(
|
||||
reader,
|
||||
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
|
||||
nodeRef, reader);
|
||||
|
||||
String mimetype = reader.getMimetype();
|
||||
// fall back if unable to resolve mimetype property
|
||||
if (mimetype == null || mimetype.length() == 0)
|
||||
{
|
||||
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
|
||||
mimetype = MIMETYPE_OCTET_STREAM;
|
||||
int extIndex = filename.lastIndexOf('.');
|
||||
if (extIndex != -1)
|
||||
{
|
||||
String ext = filename.substring(extIndex + 1);
|
||||
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
|
||||
if (mt != null)
|
||||
{
|
||||
mimetype = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
// set mimetype for the content and the character encoding for the stream
|
||||
res.setContentType(mimetype);
|
||||
res.setCharacterEncoding(reader.getEncoding());
|
||||
|
||||
// get the content and stream directly to the response output stream
|
||||
// assuming the repo is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
reader.getContent( res.getOutputStream() );
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.getMessage().contains("ClientAbortException"))
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
|
||||
}
|
||||
processDownloadRequest(req, res, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,22 +110,7 @@ public class DownloadContentServlet extends BaseServlet
|
||||
*/
|
||||
public final static String generateDownloadURL(NodeRef ref, String name)
|
||||
{
|
||||
String url = null;
|
||||
|
||||
try
|
||||
{
|
||||
url = MessageFormat.format(DOWNLOAD_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
|
||||
}
|
||||
|
||||
return url;
|
||||
return generateUrl(DOWNLOAD_URL, ref, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,21 +125,6 @@ public class DownloadContentServlet extends BaseServlet
|
||||
*/
|
||||
public final static String generateBrowserURL(NodeRef ref, String name)
|
||||
{
|
||||
String url = null;
|
||||
|
||||
try
|
||||
{
|
||||
url = MessageFormat.format(BROWSER_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
Utils.replace(URLEncoder.encode(name, "UTF-8"), "+", "%20") } );
|
||||
}
|
||||
catch (UnsupportedEncodingException uee)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
|
||||
}
|
||||
|
||||
return url;
|
||||
return generateUrl(BROWSER_URL, ref, name);
|
||||
}
|
||||
}
|
||||
|
@@ -145,12 +145,31 @@ public final class FacesHelper
|
||||
else
|
||||
{
|
||||
// make sure we do not have illegal characters in the id
|
||||
id = makeLegalId(id);
|
||||
}
|
||||
|
||||
component.setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given id a legal JSF component id by replacing illegal
|
||||
* characters with underscores.
|
||||
*
|
||||
* @param id The id to make legal
|
||||
* @return The legalised id
|
||||
*/
|
||||
public static String makeLegalId(String id)
|
||||
{
|
||||
if (id != null)
|
||||
{
|
||||
// replace illegal ID characters with an underscore
|
||||
id = id.replace(':', '_');
|
||||
id = id.replace(' ', '_');
|
||||
|
||||
// TODO: check all other illegal characters - only allowed dash and underscore
|
||||
}
|
||||
|
||||
component.setId(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Servlet responsible for streaming node content from the repo directly to the response stream.
|
||||
* The appropriate mimetype is calculated based on filename extension.
|
||||
* <p>
|
||||
* The URL to the servlet should be generated thus:
|
||||
* <pre>/alfresco/guestDownload/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
|
||||
* or
|
||||
* <pre>/alfresco/guestDownload/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
|
||||
* or
|
||||
* <pre>/alfresco/guestDownload/[direct|attach]?path=/Company%20Home/MyFolder/myfile.pdf</pre>
|
||||
* The protocol, followed by either the store and Id (NodeRef) or instead specify a name based
|
||||
* encoded Path to the content, note that the filename element is used for mimetype lookup and
|
||||
* as the returning filename for the response stream.
|
||||
* <p>
|
||||
* The 'attach' or 'direct' element is used to indicate whether to display the stream directly
|
||||
* in the browser or download it as a file attachment.
|
||||
* <p>
|
||||
* By default, the download assumes that the content is on the
|
||||
* {@link org.alfresco.model.ContentModel#PROP_CONTENT content property}.<br>
|
||||
* To retrieve the content of a specific model property, use a 'property' arg, providing the workspace,
|
||||
* node ID AND the qualified name of the property.
|
||||
* <p>
|
||||
* This servlet only accesses content available to the guest user. If the guest user does not
|
||||
* have access to the requested a 401 Forbidden response is returned to the caller.
|
||||
* <p>
|
||||
* This servlet does not effect the current session, therefore if guest access is required to a
|
||||
* resource this servlet can be used without logging out the current user.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class GuestDownloadContentServlet extends BaseDownloadContentServlet
|
||||
{
|
||||
private static final long serialVersionUID = -5258137503339817457L;
|
||||
|
||||
private static Log logger = LogFactory.getLog(GuestDownloadContentServlet.class);
|
||||
|
||||
private static final String DOWNLOAD_URL = "/guestDownload/attach/{0}/{1}/{2}/{3}";
|
||||
private static final String BROWSER_URL = "/guestDownload/direct/{0}/{1}/{2}/{3}";
|
||||
|
||||
@Override
|
||||
protected Log getLogger()
|
||||
{
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
String queryString = req.getQueryString();
|
||||
logger.debug("Setting up guest access to URL: " + req.getRequestURI() +
|
||||
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
|
||||
}
|
||||
|
||||
DownloadContentWork dcw = new DownloadContentWork(req, res);
|
||||
AuthenticationUtil.runAs(dcw, PermissionService.GUEST_AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
|
||||
* a browser should prompt the user to save the content to specified location.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateDownloadURL(NodeRef ref, String name)
|
||||
{
|
||||
return generateUrl(DOWNLOAD_URL, ref, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied directly in the reponse. This generally means a browser will
|
||||
* attempt to open the content directly if possible, else it will prompt to save the file.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name to return in the URL (cannot be null)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateBrowserURL(NodeRef ref, String name)
|
||||
{
|
||||
return generateUrl(BROWSER_URL, ref, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to wrap the call to processDownloadRequest.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class DownloadContentWork implements RunAsWork<Object>
|
||||
{
|
||||
private HttpServletRequest req = null;
|
||||
private HttpServletResponse res = null;
|
||||
|
||||
public DownloadContentWork(HttpServletRequest req, HttpServletResponse res)
|
||||
{
|
||||
this.req = req;
|
||||
this.res = res;
|
||||
}
|
||||
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
processDownloadRequest(this.req, this.res, false);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -86,8 +86,6 @@ public class TemplateContentServlet extends BaseServlet
|
||||
private static final String DEFAULT_URL = "/template/{0}/{1}/{2}";
|
||||
private static final String TEMPLATE_URL = "/template/{0}/{1}/{2}/{3}/{4}/{5}";
|
||||
|
||||
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
|
||||
|
||||
private static final String ARG_MIMETYPE = "mimetype";
|
||||
private static final String ARG_TEMPLATE_PATH = "templatePath";
|
||||
private static final String ARG_CONTEXT_PATH = "contextPath";
|
||||
@@ -95,13 +93,17 @@ public class TemplateContentServlet extends BaseServlet
|
||||
/**
|
||||
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||
protected void service(HttpServletRequest req, HttpServletResponse res)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String uri = req.getRequestURI();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
|
||||
{
|
||||
String queryString = req.getQueryString();
|
||||
logger.debug("Processing URL: " + uri +
|
||||
((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
|
||||
}
|
||||
|
||||
AuthenticationStatus status = servletAuthenticate(req, res);
|
||||
if (status == AuthenticationStatus.Failure)
|
||||
@@ -258,6 +260,7 @@ public class TemplateContentServlet extends BaseServlet
|
||||
*
|
||||
* @return an object model ready for executing template against
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object getModel(ServiceRegistry services, HttpServletRequest req, NodeRef templateRef, NodeRef nodeRef)
|
||||
{
|
||||
// build FreeMarker default model and merge
|
||||
|
@@ -22,7 +22,7 @@ public interface AjaxCommand
|
||||
* expression. Parameters required to call the method can be retrieved
|
||||
* from the request.
|
||||
*
|
||||
* Currently the content type of the response will always be text/html, in the
|
||||
* Currently the content type of the response will always be text/xml, in the
|
||||
* future sublcasses may provide a mechanism to allow the content type to be set
|
||||
* dynamically.
|
||||
*
|
||||
|
@@ -42,7 +42,7 @@ public class InvokeCommand extends BaseAjaxCommand
|
||||
|
||||
// NOTE: it doesn't seem to matter what the content type of the response is (at least with Dojo),
|
||||
// it determines it's behaviour from the mimetype specified in the AJAX call on the client,
|
||||
// therefore, for now we will always return a content type of text/html.
|
||||
// therefore, for now we will always return a content type of text/xml.
|
||||
// In the future we may use annotations on the method to be called to specify what content
|
||||
// type should be used for the response.
|
||||
|
||||
@@ -53,7 +53,7 @@ public class InvokeCommand extends BaseAjaxCommand
|
||||
RenderKit renderKit = renderFactory.getRenderKit(facesContext,
|
||||
viewRoot.getRenderKitId());
|
||||
ResponseWriter writer = renderKit.createResponseWriter(
|
||||
new OutputStreamWriter(os), MimetypeMap.MIMETYPE_HTML, "UTF-8");
|
||||
new OutputStreamWriter(os), MimetypeMap.MIMETYPE_XML, "UTF-8");
|
||||
facesContext.setResponseWriter(writer);
|
||||
response.setContentType(writer.getContentType());
|
||||
|
||||
|
@@ -848,7 +848,7 @@ public class AdvancedSearchBean
|
||||
search.addFixedValueQuery(QName.createQName(qname), strVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (value != null)
|
||||
{
|
||||
// by default use toString() value - this is for text fields and unknown types
|
||||
String strVal = value.toString();
|
||||
|
@@ -29,6 +29,7 @@ import javax.transaction.UserTransaction;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.version.VersionModel;
|
||||
import org.alfresco.repo.workflow.WorkflowModel;
|
||||
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
@@ -40,6 +41,10 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.version.Version;
|
||||
import org.alfresco.service.cmr.version.VersionType;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowTask;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.AlfrescoNavigationHandler;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.app.context.UIContextService;
|
||||
@@ -131,6 +136,14 @@ public class CheckinCheckoutBean
|
||||
this.contentService = contentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param workflowService The WorkflowService to set.
|
||||
*/
|
||||
public void setWorkflowService(WorkflowService workflowService)
|
||||
{
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The document node being used for the current operation
|
||||
*/
|
||||
@@ -357,13 +370,29 @@ public class CheckinCheckoutBean
|
||||
if (id != null && id.length() != 0)
|
||||
{
|
||||
setupContentDocument(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setDocument(null);
|
||||
}
|
||||
|
||||
clearUpload();
|
||||
resetState();
|
||||
}
|
||||
|
||||
public void setupWorkflowContentAction(ActionEvent event)
|
||||
{
|
||||
// do the common processing
|
||||
setupContentAction(event);
|
||||
|
||||
// retrieve the id of the task
|
||||
UIActionLink link = (UIActionLink)event.getComponent();
|
||||
Map<String, String> params = link.getParameterMap();
|
||||
this.workflowTaskId = params.get("taskId");
|
||||
|
||||
this.isWorkflowAction = true;
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Setup for workflow package action for task id: " + this.workflowTaskId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,7 +464,7 @@ public class CheckinCheckoutBean
|
||||
logger.debug("Checkout copy location: " + getCopyLocation());
|
||||
logger.debug("Selected Space Id: " + this.selectedSpaceId);
|
||||
}
|
||||
NodeRef workingCopyRef;
|
||||
NodeRef workingCopyRef = null;
|
||||
if (getCopyLocation().equals(COPYLOCATION_OTHER) && this.selectedSpaceId != null)
|
||||
{
|
||||
// checkout to a arbituary parent Space
|
||||
@@ -447,7 +476,31 @@ public class CheckinCheckoutBean
|
||||
}
|
||||
else
|
||||
{
|
||||
// checkout the content to the current space
|
||||
workingCopyRef = this.versionOperationsService.checkout(node.getNodeRef());
|
||||
|
||||
// if this is a workflow action and there is a task id present we need
|
||||
// to also link the working copy to the workflow package so it appears
|
||||
// in the resources panel in the manage task dialog
|
||||
if (this.isWorkflowAction && this.workflowTaskId != null)
|
||||
{
|
||||
WorkflowTask task = this.workflowService.getTaskById(this.workflowTaskId);
|
||||
if (task != null)
|
||||
{
|
||||
NodeRef workflowPackage = (NodeRef)task.properties.get(WorkflowModel.ASSOC_PACKAGE);
|
||||
if (workflowPackage != null)
|
||||
{
|
||||
this.nodeService.addChild(workflowPackage, workingCopyRef,
|
||||
ContentModel.ASSOC_CONTAINS, QName.createQName(
|
||||
NamespaceService.CONTENT_MODEL_1_0_URI,
|
||||
QName.createValidLocalName((String)this.nodeService.getProperty(
|
||||
workingCopyRef, ContentModel.PROP_NAME))));
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added working copy to workflow package: " + workflowPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the working copy Node instance
|
||||
@@ -500,7 +553,7 @@ public class CheckinCheckoutBean
|
||||
}
|
||||
|
||||
// clean up and clear action context
|
||||
clearUpload();
|
||||
resetState();
|
||||
setDocument(null);
|
||||
setWorkingDocument(null);
|
||||
|
||||
@@ -525,7 +578,7 @@ public class CheckinCheckoutBean
|
||||
if (node != null)
|
||||
{
|
||||
// clean up and clear action context
|
||||
clearUpload();
|
||||
resetState();
|
||||
setDocument(null);
|
||||
setWorkingDocument(null);
|
||||
|
||||
@@ -631,7 +684,7 @@ public class CheckinCheckoutBean
|
||||
tx.commit();
|
||||
|
||||
// clean up and clear action context
|
||||
clearUpload();
|
||||
resetState();
|
||||
setDocument(null);
|
||||
setDocumentContent(null);
|
||||
setEditorOutput(null);
|
||||
@@ -669,7 +722,7 @@ public class CheckinCheckoutBean
|
||||
// try to cancel checkout of the working copy
|
||||
this.versionOperationsService.cancelCheckout(node.getNodeRef());
|
||||
|
||||
clearUpload();
|
||||
resetState();
|
||||
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
||||
}
|
||||
@@ -718,10 +771,15 @@ public class CheckinCheckoutBean
|
||||
{
|
||||
throw new IllegalStateException("Node supplied for undo checkout has neither Working Copy or Locked aspect!");
|
||||
}
|
||||
|
||||
clearUpload();
|
||||
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
||||
|
||||
if (this.isWorkflowAction == false)
|
||||
{
|
||||
outcome = outcome + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
|
||||
}
|
||||
|
||||
resetState();
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
@@ -804,12 +862,16 @@ public class CheckinCheckoutBean
|
||||
// commit the transaction
|
||||
tx.commit();
|
||||
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
||||
|
||||
if (this.isWorkflowAction == false)
|
||||
{
|
||||
outcome = outcome + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
|
||||
}
|
||||
|
||||
// clear action context
|
||||
setDocument(null);
|
||||
clearUpload();
|
||||
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME +
|
||||
AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse";
|
||||
resetState();
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
@@ -863,7 +925,7 @@ public class CheckinCheckoutBean
|
||||
|
||||
// clear action context
|
||||
setDocument(null);
|
||||
clearUpload();
|
||||
resetState();
|
||||
|
||||
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
||||
}
|
||||
@@ -889,7 +951,7 @@ public class CheckinCheckoutBean
|
||||
public String cancel()
|
||||
{
|
||||
// reset the state
|
||||
clearUpload();
|
||||
resetState();
|
||||
|
||||
return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
||||
}
|
||||
@@ -897,7 +959,7 @@ public class CheckinCheckoutBean
|
||||
/**
|
||||
* Clear form state and upload file bean
|
||||
*/
|
||||
private void clearUpload()
|
||||
private void resetState()
|
||||
{
|
||||
// delete the temporary file we uploaded earlier
|
||||
if (this.file != null)
|
||||
@@ -912,6 +974,8 @@ public class CheckinCheckoutBean
|
||||
this.copyLocation = COPYLOCATION_CURRENT;
|
||||
this.versionNotes = "";
|
||||
this.selectedSpaceId = null;
|
||||
this.isWorkflowAction = false;
|
||||
this.workflowTaskId = null;
|
||||
|
||||
// remove the file upload bean from the session
|
||||
FacesContext ctx = FacesContext.getCurrentInstance();
|
||||
@@ -951,6 +1015,8 @@ public class CheckinCheckoutBean
|
||||
private String fileName;
|
||||
private boolean keepCheckedOut = false;
|
||||
private boolean minorChange = true;
|
||||
private boolean isWorkflowAction = false;
|
||||
private String workflowTaskId;
|
||||
private String copyLocation = COPYLOCATION_CURRENT;
|
||||
private String versionNotes = "";
|
||||
private NodeRef selectedSpaceId = null;
|
||||
@@ -969,4 +1035,7 @@ public class CheckinCheckoutBean
|
||||
|
||||
/** The ContentService to be used by the bean */
|
||||
protected ContentService contentService;
|
||||
|
||||
/** The WorkflowService to be used by the bean */
|
||||
protected WorkflowService workflowService;
|
||||
}
|
||||
|
@@ -121,15 +121,20 @@ public class ErrorBean
|
||||
*/
|
||||
public String getStackTrace()
|
||||
{
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(stringWriter);
|
||||
this.lastError.printStackTrace(writer);
|
||||
String trace = "No stack trace available";
|
||||
|
||||
// format the message for HTML display
|
||||
String trace = stringWriter.toString();
|
||||
trace = trace.replaceAll("<", "<");
|
||||
trace = trace.replaceAll(">", ">");
|
||||
trace = trace.replaceAll("\n", "<br/>");
|
||||
if (this.lastError != null)
|
||||
{
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(stringWriter);
|
||||
this.lastError.printStackTrace(writer);
|
||||
|
||||
// format the message for HTML display
|
||||
trace = stringWriter.toString();
|
||||
trace = trace.replaceAll("<", "<");
|
||||
trace = trace.replaceAll(">", ">");
|
||||
trace = trace.replaceAll("\n", "<br/>");
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
@@ -73,6 +73,7 @@ public class SpaceDetailsBean extends BaseDetailsBean
|
||||
{
|
||||
// initial state of some panels that don't use the default
|
||||
panels.put("rules-panel", false);
|
||||
panels.put("dashboard-panel", false);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -130,12 +130,12 @@ public abstract class BaseDialogBean implements IDialogBean
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getTitle()
|
||||
public String getContainerTitle()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
public String getContainerDescription()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@@ -141,7 +141,7 @@ public final class DialogManager
|
||||
public String getTitle()
|
||||
{
|
||||
// try and get the title directly from the dialog
|
||||
String title = this.currentDialogState.getDialog().getTitle();
|
||||
String title = this.currentDialogState.getDialog().getContainerTitle();
|
||||
|
||||
if (title == null)
|
||||
{
|
||||
@@ -170,7 +170,7 @@ public final class DialogManager
|
||||
public String getDescription()
|
||||
{
|
||||
// try and get the description directly from the dialog
|
||||
String desc = this.currentDialogState.getDialog().getDescription();
|
||||
String desc = this.currentDialogState.getDialog().getContainerDescription();
|
||||
|
||||
if (desc == null)
|
||||
{
|
||||
|
@@ -73,7 +73,7 @@ public interface IDialogBean
|
||||
*
|
||||
* @return The title or null if the title is to be acquired via configuration
|
||||
*/
|
||||
public String getTitle();
|
||||
public String getContainerTitle();
|
||||
|
||||
/**
|
||||
* Returns the description to be used for the dialog
|
||||
@@ -82,5 +82,5 @@ public interface IDialogBean
|
||||
*
|
||||
* @return The title or null if the title is to be acquired via configuration
|
||||
*/
|
||||
public String getDescription();
|
||||
public String getContainerDescription();
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ 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;
|
||||
|
||||
@@ -25,6 +24,7 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
|
||||
*/
|
||||
public class DatePickerGenerator extends BaseComponentGenerator
|
||||
{
|
||||
private boolean initialiseIfNull = false;
|
||||
private int yearCount = 30;
|
||||
private int startYear = new Date().getYear() + 1900 + 2;
|
||||
|
||||
@@ -61,6 +61,26 @@ public class DatePickerGenerator extends BaseComponentGenerator
|
||||
{
|
||||
this.yearCount = yearCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Determines whether the control should initially show
|
||||
* today's date if the model value is null
|
||||
*/
|
||||
public boolean isInitialiseIfNull()
|
||||
{
|
||||
return initialiseIfNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initialiseIfNull Determines whether the control should
|
||||
* initially show today's date if the model value is null.
|
||||
* This will also hide the None button thus disallowing
|
||||
* the user to set the date back to null.
|
||||
*/
|
||||
public void setInitialiseIfNull(boolean initialiseIfNull)
|
||||
{
|
||||
this.initialiseIfNull = initialiseIfNull;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UIComponent generate(FacesContext context, String id)
|
||||
@@ -71,6 +91,7 @@ public class DatePickerGenerator extends BaseComponentGenerator
|
||||
FacesHelper.setupComponentId(context, component, id);
|
||||
component.getAttributes().put("startYear", this.startYear);
|
||||
component.getAttributes().put("yearCount", this.yearCount);
|
||||
component.getAttributes().put("initialiseIfNull", new Boolean(this.initialiseIfNull));
|
||||
component.getAttributes().put("style", "margin-right: 7px;");
|
||||
|
||||
return component;
|
||||
|
@@ -15,7 +15,7 @@ import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
|
||||
*/
|
||||
public class HtmlSeparatorGenerator extends BaseComponentGenerator
|
||||
{
|
||||
protected String html = "<b>default</b>";
|
||||
protected String html = "";
|
||||
|
||||
/**
|
||||
* Returns the HTML configured to be used for this separator
|
||||
|
@@ -152,7 +152,7 @@ public final class WizardManager
|
||||
public String getTitle()
|
||||
{
|
||||
// try and get the title directly from the wizard
|
||||
String title = this.currentWizardState.getWizard().getTitle();
|
||||
String title = this.currentWizardState.getWizard().getContainerTitle();
|
||||
|
||||
if (title == null)
|
||||
{
|
||||
@@ -181,7 +181,7 @@ public final class WizardManager
|
||||
public String getDescription()
|
||||
{
|
||||
// try and get the description directly from the dialog
|
||||
String desc = this.currentWizardState.getWizard().getDescription();
|
||||
String desc = this.currentWizardState.getWizard().getContainerDescription();
|
||||
|
||||
if (desc == null)
|
||||
{
|
||||
|
@@ -26,6 +26,7 @@ import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.web.app.AlfrescoNavigationHandler;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.app.servlet.FacesHelper;
|
||||
import org.alfresco.web.bean.dialog.BaseDialogBean;
|
||||
import org.alfresco.web.bean.repository.MapNode;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
@@ -105,16 +106,7 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
this.workflowInstance = this.task.path.instance;
|
||||
|
||||
// setup the workflow package for the task
|
||||
Serializable obj = this.task.properties.get(WorkflowModel.ASSOC_PACKAGE);
|
||||
// TODO: remove this workaroud where JBPM may return a String and not the NodeRef
|
||||
if (obj instanceof NodeRef)
|
||||
{
|
||||
this.workflowPackage = (NodeRef)obj;
|
||||
}
|
||||
else if (obj instanceof String)
|
||||
{
|
||||
this.workflowPackage = new NodeRef((String)obj);
|
||||
}
|
||||
this.workflowPackage = (NodeRef)this.task.properties.get(WorkflowModel.ASSOC_PACKAGE);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
@@ -208,7 +200,7 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
@Override
|
||||
public String getFinishButtonLabel()
|
||||
{
|
||||
return Application.getMessage(FacesContext.getCurrentInstance(), "save");
|
||||
return Application.getMessage(FacesContext.getCurrentInstance(), "save_changes");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,7 +210,7 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle()
|
||||
public String getContainerTitle()
|
||||
{
|
||||
String titleStart = Application.getMessage(FacesContext.getCurrentInstance(), "manage_task_title");
|
||||
|
||||
@@ -226,7 +218,7 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription()
|
||||
public String getContainerDescription()
|
||||
{
|
||||
return this.task.description;
|
||||
}
|
||||
@@ -251,7 +243,7 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
String selectedTransition = null;
|
||||
for (WorkflowTransition trans : this.transitions)
|
||||
{
|
||||
Object result = reqParams.get(CLIENT_ID_PREFIX + trans.title);
|
||||
Object result = reqParams.get(CLIENT_ID_PREFIX + FacesHelper.makeLegalId(trans.title));
|
||||
if (result != null)
|
||||
{
|
||||
// this was the button that was pressed
|
||||
@@ -635,7 +627,10 @@ public class ManageTaskDialog extends BaseDialogBean
|
||||
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);
|
||||
// node.addPropertyResolver("completed", this.completeResolver);
|
||||
|
||||
// add the id of the task being managed
|
||||
node.getProperties().put("taskId", this.task.id);
|
||||
|
||||
this.resources.add(node);
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ public class ReassignTaskDialog extends BaseDialogBean
|
||||
{
|
||||
super.init(parameters);
|
||||
|
||||
this.taskId = this.parameters.get("task-id");
|
||||
this.taskId = this.parameters.get("id");
|
||||
if (this.taskId == null || this.taskId.length() == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Reassign task dialog called without task id");
|
||||
|
@@ -46,6 +46,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
public class StartWorkflowWizard extends BaseWizardBean
|
||||
{
|
||||
protected String selectedWorkflow;
|
||||
protected String previouslySelectedWorkflow;
|
||||
protected List<SelectItem> availableWorkflows;
|
||||
protected Map<String, WorkflowDefinition> workflows;
|
||||
protected WorkflowService workflowService;
|
||||
@@ -77,18 +78,13 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
this.selectedWorkflow = null;
|
||||
}
|
||||
|
||||
this.previouslySelectedWorkflow = null;
|
||||
this.startTaskNode = null;
|
||||
this.resources = null;
|
||||
this.itemsToAdd = null;
|
||||
this.packageItemsToAdd = new ArrayList<String>();
|
||||
this.isItemBeingAdded = false;
|
||||
if (this.packageItemsRichList != null)
|
||||
{
|
||||
this.packageItemsRichList.setValue(null);
|
||||
this.packageItemsRichList = null;
|
||||
}
|
||||
|
||||
// TODO: Does this need to be in a read-only transaction??
|
||||
resetRichList();
|
||||
|
||||
// add the item the workflow wizard was started on to the list of resources
|
||||
String itemToWorkflowId = this.parameters.get("item-to-workflow");
|
||||
@@ -110,11 +106,7 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
public void restored()
|
||||
{
|
||||
// reset the workflow package rich list so everything gets re-evaluated
|
||||
if (this.packageItemsRichList != null)
|
||||
{
|
||||
this.packageItemsRichList.setValue(null);
|
||||
this.packageItemsRichList = null;
|
||||
}
|
||||
resetRichList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,13 +182,14 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
{
|
||||
String stepName = Application.getWizardManager().getCurrentStepName();
|
||||
|
||||
if ("options".equals(stepName) && this.startTaskNode == null)
|
||||
if ("options".equals(stepName) &&
|
||||
(this.selectedWorkflow.equals(this.previouslySelectedWorkflow) == false))
|
||||
{
|
||||
// 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);
|
||||
WorkflowDefinition flowDef = this.workflows.get(this.selectedWorkflow);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Selected workflow: "+ flowDef);
|
||||
@@ -211,11 +204,29 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
this.startTaskNode = new TransientNode(taskDef.metadata.getName(),
|
||||
"task_" + System.currentTimeMillis(), null);
|
||||
}
|
||||
|
||||
// we also need to reset the resources list so that the actions get re-evaluated
|
||||
resetRichList();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String back()
|
||||
{
|
||||
String stepName = Application.getWizardManager().getCurrentStepName();
|
||||
|
||||
// if we have come back to the "choose-workflow" step remember
|
||||
// the current workflow selection
|
||||
if ("choose-workflow".equals(stepName))
|
||||
{
|
||||
this.previouslySelectedWorkflow = this.selectedWorkflow;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNextButtonDisabled()
|
||||
{
|
||||
@@ -293,7 +304,7 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
// reset the rich list so it re-renders
|
||||
this.packageItemsRichList.setValue(null);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Bean Getters and Setters
|
||||
|
||||
@@ -465,33 +476,34 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
*/
|
||||
public List<SelectItem> getStartableWorkflows()
|
||||
{
|
||||
if (this.availableWorkflows == null)
|
||||
// NOTE: we don't cache the list of startable workflows as they could get
|
||||
// updated, in which case we need the latest instance id, they could
|
||||
// theoretically also get removed.
|
||||
|
||||
this.availableWorkflows = new ArrayList<SelectItem>(4);
|
||||
this.workflows = new HashMap<String, WorkflowDefinition>(4);
|
||||
|
||||
List<WorkflowDefinition> workflowDefs = this.workflowService.getDefinitions();
|
||||
for (WorkflowDefinition workflowDef : workflowDefs)
|
||||
{
|
||||
this.availableWorkflows = new ArrayList<SelectItem>(4);
|
||||
this.workflows = new HashMap<String, WorkflowDefinition>(4);
|
||||
|
||||
List<WorkflowDefinition> workflowDefs = this.workflowService.getDefinitions();
|
||||
for (WorkflowDefinition workflowDef : workflowDefs)
|
||||
String label = workflowDef.title;
|
||||
if (workflowDef.description != null && workflowDef.description.length() > 0)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
@@ -559,4 +571,19 @@ public class StartWorkflowWizard extends BaseWizardBean
|
||||
{
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* Resets the rich list
|
||||
*/
|
||||
protected void resetRichList()
|
||||
{
|
||||
if (this.packageItemsRichList != null)
|
||||
{
|
||||
this.packageItemsRichList.setValue(null);
|
||||
this.packageItemsRichList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ public class ViewCompletedTaskDialog extends ManageTaskDialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle()
|
||||
public String getContainerTitle()
|
||||
{
|
||||
String titleStart = Application.getMessage(FacesContext.getCurrentInstance(), "view_completed_task_title");
|
||||
|
||||
|
@@ -196,7 +196,10 @@ public class WorkflowBean
|
||||
}
|
||||
|
||||
// add the targets for this particular association
|
||||
params.put(assocQName, (Serializable)targets);
|
||||
if (targets.size() > 0)
|
||||
{
|
||||
params.put(assocQName, (Serializable)targets);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
@@ -223,19 +226,8 @@ public class WorkflowBean
|
||||
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)
|
||||
NodeRef context = (NodeRef)task.properties.get(WorkflowModel.PROP_CONTEXT);
|
||||
if (context != null && this.nodeService.exists(context))
|
||||
{
|
||||
String name = Repository.getNameForNode(this.nodeService, context);
|
||||
node.getProperties().put("sourceSpaceName", name);
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package org.alfresco.web.data;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Timestamp;
|
||||
import java.text.CollationKey;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
@@ -175,6 +176,10 @@ public abstract class Sort
|
||||
{
|
||||
this.comparator = new FloatComparator();
|
||||
}
|
||||
else if (returnType.equals(Timestamp.class))
|
||||
{
|
||||
this.comparator = new TimestampComparator();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_logger.warn("Unsupported sort data type: " + returnType + " defaulting to .toString()");
|
||||
@@ -408,6 +413,20 @@ public abstract class Sort
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimestampComparator implements Comparator
|
||||
{
|
||||
/**
|
||||
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public int compare(final Object obj1, final Object obj2)
|
||||
{
|
||||
if (obj1 == null && obj2 == null) return 0;
|
||||
if (obj1 == null) return -1;
|
||||
if (obj2 == null) return 1;
|
||||
return ((Timestamp)obj1).compareTo((Timestamp)obj2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private Data
|
||||
|
@@ -55,6 +55,7 @@ import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.model.FileInfo;
|
||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
import org.alfresco.service.cmr.repository.NoTransformerException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
@@ -968,7 +969,8 @@ public final class Utils
|
||||
if (err != null)
|
||||
{
|
||||
if ((err instanceof InvalidNodeRefException == false &&
|
||||
err instanceof AccessDeniedException == false) || logger.isDebugEnabled())
|
||||
err instanceof AccessDeniedException == false &&
|
||||
err instanceof NoTransformerException == false) || logger.isDebugEnabled())
|
||||
{
|
||||
logger.error(msg, err);
|
||||
}
|
||||
|
@@ -111,165 +111,175 @@ public class ActionLinkRenderer extends BaseRenderer
|
||||
*/
|
||||
private String renderActionLink(FacesContext context, UIActionLink link)
|
||||
{
|
||||
Map attrs = link.getAttributes();
|
||||
StringBuilder linkBuf = new StringBuilder(256);
|
||||
// if there is no value for the link there will be no visible output
|
||||
// on the page so don't bother rendering anything
|
||||
String linkHtml = "";
|
||||
Object linkValue = link.getValue();
|
||||
|
||||
if (link.getHref() == null)
|
||||
if (linkValue != null)
|
||||
{
|
||||
linkBuf.append("<a href='#' onclick=\"");
|
||||
Map attrs = link.getAttributes();
|
||||
StringBuilder linkBuf = new StringBuilder(256);
|
||||
|
||||
// if we have an overriden onclick add that
|
||||
if (link.getOnclick() != null)
|
||||
if (link.getHref() == null)
|
||||
{
|
||||
linkBuf.append(link.getOnclick());
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
linkBuf.append(Utils.generateFormSubmit(context, link, Utils.getActionHiddenFieldName(context, link), link.getClientId(context), getParameterComponents(link)));
|
||||
}
|
||||
|
||||
linkBuf.append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
String href = link.getHref();
|
||||
|
||||
// prefix the web context path if required
|
||||
linkBuf.append("<a href=\"");
|
||||
if (href.startsWith("/"))
|
||||
{
|
||||
linkBuf.append(context.getExternalContext().getRequestContextPath());
|
||||
}
|
||||
linkBuf.append(href);
|
||||
|
||||
// append arguments if specified
|
||||
Map<String, String> actionParams = getParameterComponents(link);
|
||||
if (actionParams != null)
|
||||
{
|
||||
boolean first = (href.indexOf('?') == -1);
|
||||
for (String name : actionParams.keySet())
|
||||
linkBuf.append("<a href='#' onclick=\"");
|
||||
|
||||
// if we have an overriden onclick add that
|
||||
if (link.getOnclick() != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
linkBuf.append(" target=\"")
|
||||
.append(link.getTarget())
|
||||
.append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
linkBuf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
linkBuf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
if (link.getTooltip() != null)
|
||||
{
|
||||
linkBuf.append(" title=\"")
|
||||
.append(Utils.encode(link.getTooltip()))
|
||||
.append('"');
|
||||
}
|
||||
linkBuf.append('>');
|
||||
|
||||
StringBuilder buf = new StringBuilder(350);
|
||||
if (link.getImage() != null)
|
||||
{
|
||||
int padding = link.getPadding();
|
||||
if (padding != 0)
|
||||
{
|
||||
// TODO: make this width value a property!
|
||||
buf.append("<table cellspacing=0 cellpadding=0><tr><td width=16>");
|
||||
}
|
||||
|
||||
if (link.getShowLink() == false)
|
||||
{
|
||||
buf.append(linkBuf.toString());
|
||||
}
|
||||
|
||||
// TODO: allow configuring of alignment attribute
|
||||
buf.append(Utils.buildImageTag(context, link.getImage(), (String)link.getValue(), "absmiddle"));
|
||||
|
||||
if (link.getShowLink() == false)
|
||||
{
|
||||
buf.append("</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (padding != 0)
|
||||
{
|
||||
buf.append("</td><td style=\"padding:")
|
||||
.append(padding)
|
||||
.append("px\">");
|
||||
linkBuf.append(link.getOnclick());
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add horizontal spacing as component property
|
||||
buf.append("<span style='padding-left:2px");
|
||||
|
||||
// text next to an image may need alignment
|
||||
if (attrs.get("verticalAlign") != null)
|
||||
{
|
||||
buf.append(";vertical-align:")
|
||||
.append(attrs.get("verticalAlign"));
|
||||
}
|
||||
|
||||
buf.append("'>");
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
linkBuf.append(Utils.generateFormSubmit(context, link, Utils.getActionHiddenFieldName(context, link), link.getClientId(context), getParameterComponents(link)));
|
||||
}
|
||||
|
||||
buf.append(linkBuf.toString());
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
linkBuf.append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
String href = link.getHref();
|
||||
|
||||
if (padding == 0)
|
||||
// prefix the web context path if required
|
||||
linkBuf.append("<a href=\"");
|
||||
if (href.startsWith("/"))
|
||||
{
|
||||
buf.append("</span>");
|
||||
linkBuf.append(context.getExternalContext().getRequestContextPath());
|
||||
}
|
||||
linkBuf.append(href);
|
||||
|
||||
// append arguments if specified
|
||||
Map<String, String> 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)
|
||||
{
|
||||
linkBuf.append(" target=\"")
|
||||
.append(link.getTarget())
|
||||
.append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (padding != 0)
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
buf.append("</td></tr></table>");
|
||||
linkBuf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.append(linkBuf.toString());
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
linkBuf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
if (link.getTooltip() != null)
|
||||
{
|
||||
linkBuf.append(" title=\"")
|
||||
.append(Utils.encode(link.getTooltip()))
|
||||
.append('"');
|
||||
}
|
||||
linkBuf.append('>');
|
||||
|
||||
StringBuilder buf = new StringBuilder(350);
|
||||
if (link.getImage() != null)
|
||||
{
|
||||
int padding = link.getPadding();
|
||||
if (padding != 0)
|
||||
{
|
||||
// TODO: make this width value a property!
|
||||
buf.append("<table cellspacing=0 cellpadding=0><tr><td width=16>");
|
||||
}
|
||||
|
||||
if (link.getShowLink() == false)
|
||||
{
|
||||
buf.append(linkBuf.toString());
|
||||
}
|
||||
|
||||
// TODO: allow configuring of alignment attribute
|
||||
buf.append(Utils.buildImageTag(context, link.getImage(), (String)link.getValue(), "absmiddle"));
|
||||
|
||||
if (link.getShowLink() == false)
|
||||
{
|
||||
buf.append("</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (padding != 0)
|
||||
{
|
||||
buf.append("</td><td style=\"padding:")
|
||||
.append(padding)
|
||||
.append("px\">");
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add horizontal spacing as component property
|
||||
buf.append("<span style='padding-left:2px");
|
||||
|
||||
// text next to an image may need alignment
|
||||
if (attrs.get("verticalAlign") != null)
|
||||
{
|
||||
buf.append(";vertical-align:")
|
||||
.append(attrs.get("verticalAlign"));
|
||||
}
|
||||
|
||||
buf.append("'>");
|
||||
}
|
||||
|
||||
buf.append(linkBuf.toString());
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
|
||||
if (padding == 0)
|
||||
{
|
||||
buf.append("</span>");
|
||||
}
|
||||
}
|
||||
|
||||
if (padding != 0)
|
||||
{
|
||||
buf.append("</td></tr></table>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.append(linkBuf.toString());
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
}
|
||||
|
||||
linkHtml = buf.toString();
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
return linkHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,71 +292,81 @@ public class ActionLinkRenderer extends BaseRenderer
|
||||
*/
|
||||
private String renderMenuAction(FacesContext context, UIActionLink link, int padding)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
// if there is no value for the link there will be no visible output
|
||||
// on the page so don't bother rendering anything
|
||||
String linkHtml = "";
|
||||
Object linkValue = link.getValue();
|
||||
|
||||
buf.append("<tr><td>");
|
||||
|
||||
// render image cell first for a menu
|
||||
if (link.getImage() != null)
|
||||
if (linkValue != null)
|
||||
{
|
||||
buf.append(Utils.buildImageTag(context, link.getImage(), (String)link.getValue()));
|
||||
}
|
||||
|
||||
buf.append("</td><td");
|
||||
if (padding != 0)
|
||||
{
|
||||
buf.append(" style=\"padding:")
|
||||
.append(padding)
|
||||
.append("px\"");
|
||||
}
|
||||
buf.append(">");
|
||||
|
||||
// render text link cell for the menu
|
||||
if (link.getHref() == null)
|
||||
{
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
buf.append(Utils.generateFormSubmit(context, link, Utils.getActionHiddenFieldName(context, link), link.getClientId(context), getParameterComponents(link)));
|
||||
buf.append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
String href = link.getHref();
|
||||
if (href.startsWith("http") == false)
|
||||
{
|
||||
href = context.getExternalContext().getRequestContextPath() + href;
|
||||
}
|
||||
buf.append("<a href=\"")
|
||||
.append(href)
|
||||
.append('"');
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
|
||||
// output href 'target' attribute if supplied
|
||||
if (link.getTarget() != null)
|
||||
buf.append("<tr><td>");
|
||||
|
||||
// render image cell first for a menu
|
||||
if (link.getImage() != null)
|
||||
{
|
||||
buf.append(" target=\"")
|
||||
.append(link.getTarget())
|
||||
.append("\"");
|
||||
buf.append(Utils.buildImageTag(context, link.getImage(), (String)link.getValue()));
|
||||
}
|
||||
|
||||
buf.append("</td><td");
|
||||
if (padding != 0)
|
||||
{
|
||||
buf.append(" style=\"padding:")
|
||||
.append(padding)
|
||||
.append("px\"");
|
||||
}
|
||||
buf.append(">");
|
||||
|
||||
// render text link cell for the menu
|
||||
if (link.getHref() == null)
|
||||
{
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
buf.append(Utils.generateFormSubmit(context, link, Utils.getActionHiddenFieldName(context, link), link.getClientId(context), getParameterComponents(link)));
|
||||
buf.append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
String href = link.getHref();
|
||||
if (href.startsWith("http") == false)
|
||||
{
|
||||
href = context.getExternalContext().getRequestContextPath() + href;
|
||||
}
|
||||
buf.append("<a href=\"")
|
||||
.append(href)
|
||||
.append('"');
|
||||
|
||||
// output href 'target' attribute if supplied
|
||||
if (link.getTarget() != null)
|
||||
{
|
||||
buf.append(" target=\"")
|
||||
.append(link.getTarget())
|
||||
.append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
Map attrs = link.getAttributes();
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
buf.append('>');
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
|
||||
buf.append("</td></tr>");
|
||||
|
||||
linkHtml = buf.toString();
|
||||
}
|
||||
|
||||
Map attrs = link.getAttributes();
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
buf.append('>');
|
||||
buf.append(Utils.encode(link.getValue().toString()));
|
||||
buf.append("</a>");
|
||||
|
||||
buf.append("</td></tr>");
|
||||
|
||||
return buf.toString();
|
||||
return linkHtml;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -37,8 +37,6 @@ 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
|
||||
@@ -58,8 +56,6 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
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)
|
||||
@@ -168,6 +164,7 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
* input component must render the submitted value if it's set, and use the local
|
||||
* value only if there is no submitted value.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void encodeBegin(FacesContext context, UIComponent component)
|
||||
throws IOException
|
||||
{
|
||||
@@ -178,6 +175,7 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
String clientId = component.getClientId(context);
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
String cmdFieldName = clientId + FIELD_CMD;
|
||||
Boolean initIfNull = (Boolean)component.getAttributes().get("initialiseIfNull");
|
||||
|
||||
// this is part of the spec:
|
||||
// first you attempt to build the date from the submitted value
|
||||
@@ -188,12 +186,19 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
}
|
||||
else
|
||||
{
|
||||
// second if no submitted value is found, default to the current value
|
||||
// second - if no submitted value is found, default to the current value
|
||||
Object value = ((ValueHolder)component).getValue();
|
||||
if (value instanceof Date)
|
||||
{
|
||||
date = (Date)value;
|
||||
}
|
||||
|
||||
// third - if no date is present and the initialiseIfNull attribute
|
||||
// is set to true set the date to today's date
|
||||
if (date == null && initIfNull != null && initIfNull.booleanValue())
|
||||
{
|
||||
date = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
// create a flag to show if the component is disabled
|
||||
@@ -253,18 +258,23 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
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
|
||||
// date back to null (if initialiseIfNull is false) or to select today's date
|
||||
if (disabled.booleanValue() == false)
|
||||
{
|
||||
out.write("<input type=\"button\" onclick=\"");
|
||||
out.write(Utils.generateFormSubmit(context, component, cmdFieldName, Integer.toString(CMD_TODAY)));
|
||||
out.write("\" value=\"");
|
||||
out.write(Application.getMessage(context, "today"));
|
||||
out.write("\"> <input type=\"button\" onclick=\"");
|
||||
out.write(Utils.generateFormSubmit(context, component, cmdFieldName, Integer.toString(CMD_RESET)));
|
||||
out.write("\" value=\"");
|
||||
out.write(Application.getMessage(context, "none"));
|
||||
out.write("\">");
|
||||
out.write("\"> ");
|
||||
|
||||
if (initIfNull != null && initIfNull.booleanValue() == false)
|
||||
{
|
||||
out.write("<input type=\"button\" onclick=\"");
|
||||
out.write(Utils.generateFormSubmit(context, component, cmdFieldName, Integer.toString(CMD_RESET)));
|
||||
out.write("\" value=\"");
|
||||
out.write(Application.getMessage(context, "none"));
|
||||
out.write("\">");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -412,7 +422,7 @@ public class DatePickerRenderer extends BaseRenderer
|
||||
Locale locale = Application.getLanguage(FacesContext.getCurrentInstance());
|
||||
if (locale == null)
|
||||
{
|
||||
locale = locale.getDefault();
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
DateFormatSymbols dfs = new DateFormatSymbols(locale);
|
||||
String[] names = dfs.getMonths();
|
||||
|
@@ -60,6 +60,7 @@ public class InputDatePickerTag extends HtmlComponentTag
|
||||
this.value = null;
|
||||
this.showTime = null;
|
||||
this.disabled = null;
|
||||
this.initIfNull = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +76,7 @@ public class InputDatePickerTag extends HtmlComponentTag
|
||||
setStringProperty(component, "value", this.value);
|
||||
setBooleanProperty(component, "showTime", this.showTime);
|
||||
setBooleanProperty(component, "disabled", this.disabled);
|
||||
setBooleanProperty(component, "initialiseIfNull", this.initIfNull);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,9 +129,22 @@ public class InputDatePickerTag extends HtmlComponentTag
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether today's date should be shown initially if the underlying
|
||||
* model value is null. This will also hide the None button thus disallowing
|
||||
* the user to set the date back to null.
|
||||
*
|
||||
* @param initialiseIfNull true to show today's date instead of 'None'
|
||||
*/
|
||||
public void setInitialiseIfNull(String initialiseIfNull)
|
||||
{
|
||||
this.initIfNull = initialiseIfNull;
|
||||
}
|
||||
|
||||
private String startYear = null;
|
||||
private String yearCount = null;
|
||||
private String value = null;
|
||||
private String showTime = null;
|
||||
private String disabled = null;
|
||||
private String initIfNull = null;
|
||||
}
|
||||
|
@@ -186,6 +186,9 @@ public class UIDialogButtons extends SelfRenderingComponent
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Adding " + buttons.size() + " additional buttons: " + buttons);
|
||||
|
||||
// add a spacing row to separate the additional buttons from the OK button
|
||||
addSpacingRow(context);
|
||||
|
||||
for (DialogButtonConfig buttonCfg : buttons)
|
||||
{
|
||||
UICommand button = (UICommand)context.getApplication().
|
||||
|
@@ -319,6 +319,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
|
||||
inputFromDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
inputFromDate.setValueBinding("startYear", startYearBind);
|
||||
inputFromDate.setValueBinding("yearCount", yearCountBind);
|
||||
inputFromDate.getAttributes().put("initialiseIfNull", Boolean.TRUE);
|
||||
inputFromDate.getAttributes().put("showTime", showTime);
|
||||
ValueBinding vbFromDate = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + PREFIX_DATE_FROM + propDef.getName().toString() + "\"]}");
|
||||
@@ -338,6 +339,7 @@ public class UISearchCustomProperties extends SelfRenderingComponent implements
|
||||
inputToDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
inputToDate.setValueBinding("startYear", startYearBind);
|
||||
inputToDate.setValueBinding("yearCount", yearCountBind);
|
||||
inputToDate.getAttributes().put("initialiseIfNull", Boolean.TRUE);
|
||||
inputToDate.getAttributes().put("showTime", showTime);
|
||||
ValueBinding vbToDate = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + PREFIX_DATE_TO + propDef.getName().toString() + "\"]}");
|
||||
|
@@ -103,19 +103,25 @@ public class UIWorkflowSummary extends SelfRenderingComponent
|
||||
out.write(userName);
|
||||
}
|
||||
out.write("</td></tr><tr><td>");
|
||||
out.write(bundle.getString("start_date"));
|
||||
out.write(bundle.getString("started_on"));
|
||||
out.write(":</td><td>");
|
||||
if (wi.startDate != null)
|
||||
{
|
||||
out.write(format.format(wi.startDate));
|
||||
}
|
||||
out.write("</td></tr><tr><td>");
|
||||
out.write(bundle.getString("due_date"));
|
||||
out.write(bundle.getString("completed_on"));
|
||||
out.write(":</td><td>");
|
||||
if (wi.endDate != null)
|
||||
{
|
||||
out.write(format.format(wi.endDate));
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write("<");
|
||||
out.write(bundle.getString("in_progress"));
|
||||
out.write(">");
|
||||
}
|
||||
out.write("</td></tr></table>");
|
||||
}
|
||||
}
|
||||
|
@@ -75,6 +75,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
private Boolean validationEnabled;
|
||||
private String mode;
|
||||
private String configArea;
|
||||
private String nextButtonId;
|
||||
private String finishButtonId;
|
||||
|
||||
/**
|
||||
@@ -219,6 +220,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
this.validationEnabled = (Boolean)values[7];
|
||||
this.validations = (List<ClientValidation>)values[8];
|
||||
this.finishButtonId = (String)values[9];
|
||||
this.nextButtonId = (String)values[10];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +228,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[10];
|
||||
Object values[] = new Object[11];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.nodeRef;
|
||||
@@ -238,6 +240,7 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
values[7] = this.validationEnabled;
|
||||
values[8] = this.validations;
|
||||
values[9] = this.finishButtonId;
|
||||
values[10] = this.nextButtonId;
|
||||
return (values);
|
||||
}
|
||||
|
||||
@@ -391,6 +394,26 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
{
|
||||
this.finishButtonId = finishButtonId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the next button
|
||||
*
|
||||
* @return The id of the next button on the page
|
||||
*/
|
||||
public String getNextButtonId()
|
||||
{
|
||||
return this.nextButtonId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the next button being used on the page
|
||||
*
|
||||
* @param nextButtonId The id of the next button
|
||||
*/
|
||||
public void setNextButtonId(String nextButtonId)
|
||||
{
|
||||
this.nextButtonId = nextButtonId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the mode
|
||||
@@ -487,8 +510,12 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
UIForm form = Utils.getParentForm(context, this);
|
||||
|
||||
// TODO: We need to encode all the JavaScript functions here
|
||||
// with the client id of the property sheet so that we
|
||||
// can potentially add more than one property sheet to
|
||||
// page and have validation function correctly.
|
||||
|
||||
// output the validation.js script
|
||||
// TODO: make sure its only included once per page!!
|
||||
out.write("\n<script type='text/javascript' src='");
|
||||
out.write(context.getExternalContext().getRequestContextPath());
|
||||
out.write("/scripts/validation.js");
|
||||
@@ -496,9 +523,11 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
|
||||
// output variable to hold flag for which submit button was pressed
|
||||
out.write("var finishButtonPressed = false;\n");
|
||||
out.write("var nextButtonPressed = false;\n");
|
||||
|
||||
// output the validate() function
|
||||
out.write("function validate()\n{\n var result = true;\n if (finishButtonPressed && (");
|
||||
out.write("function validate()\n{\n var result = true;\n ");
|
||||
out.write("if ((finishButtonPressed || nextButtonPressed) && (");
|
||||
|
||||
int numberValidations = this.validations.size();
|
||||
List<ClientValidation> realTimeValidations =
|
||||
@@ -518,7 +547,8 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
|
||||
// return false if validation failed to stop the form submitting
|
||||
out.write(")\n { result = false; }\n\n");
|
||||
out.write(" finishButtonPressed = false;\n return result;\n}\n\n");
|
||||
out.write(" finishButtonPressed = false;\n nextButtonPressed = false;\n");
|
||||
out.write(" return result;\n}\n\n");
|
||||
|
||||
// output the processButtonState() function (if necessary)
|
||||
int numberRealTimeValidations = realTimeValidations.size();
|
||||
@@ -532,17 +562,41 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
(x == (numberRealTimeValidations-1)), false);
|
||||
}
|
||||
|
||||
// disable the finish button if validation failed
|
||||
out.write("\n { document.getElementById('");
|
||||
// disable the finish button if validation failed and
|
||||
// also the next button if it is present
|
||||
|
||||
|
||||
out.write("\n {\n document.getElementById('");
|
||||
out.write(form.getClientId(context));
|
||||
out.write(NamingContainer.SEPARATOR_CHAR);
|
||||
out.write(getFinishButtonId());
|
||||
out.write("').disabled = true; }\n");
|
||||
out.write(" else { document.getElementById('");
|
||||
out.write("').disabled = true; \n");
|
||||
if (this.nextButtonId != null && this.nextButtonId.length() > 0)
|
||||
{
|
||||
out.write(" document.getElementById('");
|
||||
out.write(form.getClientId(context));
|
||||
out.write(NamingContainer.SEPARATOR_CHAR);
|
||||
out.write(this.nextButtonId);
|
||||
out.write("').disabled = true; \n");
|
||||
}
|
||||
out.write(" }\n");
|
||||
|
||||
out.write(" else\n {\n document.getElementById('");
|
||||
out.write(form.getClientId(context));
|
||||
out.write(NamingContainer.SEPARATOR_CHAR);
|
||||
out.write(getFinishButtonId());
|
||||
out.write("').disabled = false; }\n}\n\n");
|
||||
out.write("').disabled = false;");
|
||||
|
||||
if (this.nextButtonId != null && this.nextButtonId.length() > 0)
|
||||
{
|
||||
out.write("\n document.getElementById('");
|
||||
out.write(form.getClientId(context));
|
||||
out.write(NamingContainer.SEPARATOR_CHAR);
|
||||
out.write(this.nextButtonId);
|
||||
out.write("').disabled = false;");
|
||||
}
|
||||
|
||||
out.write("\n }\n}\n\n");
|
||||
}
|
||||
|
||||
// write out a function to initialise everything
|
||||
@@ -560,6 +614,16 @@ public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
out.write(getFinishButtonId());
|
||||
out.write("').onclick = function() { finishButtonPressed = true; }\n");
|
||||
|
||||
// set the flag when the finish button is clicked
|
||||
if (this.nextButtonId != null && this.nextButtonId.length() > 0)
|
||||
{
|
||||
out.write(" document.getElementById('");
|
||||
out.write(form.getClientId(context));
|
||||
out.write(NamingContainer.SEPARATOR_CHAR);
|
||||
out.write(this.nextButtonId);
|
||||
out.write("').onclick = function() { nextButtonPressed = true; }\n");
|
||||
}
|
||||
|
||||
// perform an initial check at page load time (if we have any real time validations)
|
||||
if (numberRealTimeValidations > 0)
|
||||
{
|
||||
|
@@ -39,6 +39,7 @@ public class PropertySheetGridTag extends BaseComponentTag
|
||||
private String cellpadding;
|
||||
private String cellspacing;
|
||||
private String finishButtonId;
|
||||
private String nextButtonId;
|
||||
|
||||
/**
|
||||
* @see javax.faces.webapp.UIComponentTag#getComponentType()
|
||||
@@ -144,6 +145,14 @@ public class PropertySheetGridTag extends BaseComponentTag
|
||||
this.cellspacing = cellspacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nextButtonId Sets the next button id
|
||||
*/
|
||||
public void setNextButtonId(String nextButtonId)
|
||||
{
|
||||
this.nextButtonId = nextButtonId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param finishButtonId Sets the finish button id
|
||||
*/
|
||||
@@ -171,6 +180,7 @@ public class PropertySheetGridTag extends BaseComponentTag
|
||||
setStringStaticProperty(component, "cellpadding", this.cellpadding);
|
||||
setStringStaticProperty(component, "cellspacing", this.cellspacing);
|
||||
setStringStaticProperty(component, "finishButtonId", this.finishButtonId);
|
||||
setStringStaticProperty(component, "nextButtonId", this.nextButtonId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,6 +200,7 @@ public class PropertySheetGridTag extends BaseComponentTag
|
||||
this.cellpadding = null;
|
||||
this.cellspacing = null;
|
||||
this.finishButtonId = null;
|
||||
this.nextButtonId = null;
|
||||
|
||||
super.release();
|
||||
}
|
||||
|
@@ -73,6 +73,17 @@
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<description>The number of years to display in the list</description>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>initialiseIfNull</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<description>
|
||||
Whether to initialise the control showing today's date if the
|
||||
underlying value in the model is null. This will also hide the
|
||||
None button thus disallowing the user to set the date back to null.
|
||||
</description>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
|
@@ -1021,6 +1021,10 @@
|
||||
<property-name>contentService</property-name>
|
||||
<value>#{ContentService}</value>
|
||||
</managed-property>
|
||||
<managed-property>
|
||||
<property-name>workflowService</property-name>
|
||||
<value>#{WorkflowService}</value>
|
||||
</managed-property>
|
||||
</managed-bean>
|
||||
|
||||
<managed-bean>
|
||||
@@ -2071,6 +2075,10 @@
|
||||
<property-name>yearCount</property-name>
|
||||
<value>30</value>
|
||||
</managed-property>
|
||||
<managed-property>
|
||||
<property-name>initialiseIfNull</property-name>
|
||||
<value>false</value>
|
||||
</managed-property>
|
||||
-->
|
||||
</managed-bean>
|
||||
|
||||
@@ -2090,6 +2098,10 @@
|
||||
<property-name>yearCount</property-name>
|
||||
<value>30</value>
|
||||
</managed-property>
|
||||
<managed-property>
|
||||
<property-name>initialiseIfNull</property-name>
|
||||
<value>false</value>
|
||||
</managed-property>
|
||||
-->
|
||||
</managed-bean>
|
||||
|
||||
|
@@ -61,6 +61,12 @@
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>nextButtonId</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
|
@@ -91,22 +91,18 @@
|
||||
<filter-class>org.alfresco.repo.webdav.auth.NTLMAuthenticationFilter</filter-class>
|
||||
-->
|
||||
</filter>
|
||||
|
||||
<!-- -->
|
||||
<!-- JBPM START -->
|
||||
<!-- Used by the JPBM console for monitoring in-flight workflows -->
|
||||
<!-- TODO: Remove this block - just a temporary placement for testing -->
|
||||
<!-- -->
|
||||
|
||||
<filter>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<filter-class>org.alfresco.web.app.servlet.AdminAuthenticationFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<!-- Used by the JPBM console for monitoring in-flight workflows -->
|
||||
<filter>
|
||||
<filter-name>JbpmContextFilter</filter-name>
|
||||
<filter-class>org.jbpm.webapp.filter.JbpmContextFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<!-- -->
|
||||
<!-- JBPM END -->
|
||||
<!-- -->
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/*</url-pattern>
|
||||
@@ -126,21 +122,47 @@
|
||||
<url-pattern>/webdav/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- -->
|
||||
<!-- JBPM START -->
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/admin/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/categories/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/groups/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/users/delete-user.jsp</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/users/users.jsp</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jsp/dialog/system-info.jsp</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>Admin Authentication Filter</filter-name>
|
||||
<url-pattern>/faces/jbpm/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- Used by the JPBM console for monitoring in-flight workflows -->
|
||||
<!-- TODO: Remove this block - just a temporary placement for testing -->
|
||||
<!-- -->
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>JbpmContextFilter</filter-name>
|
||||
<url-pattern>/faces/jbpm/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- -->
|
||||
<!-- JBPM END -->
|
||||
<!-- -->
|
||||
|
||||
<listener>
|
||||
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
|
||||
</listener>
|
||||
@@ -170,6 +192,11 @@
|
||||
<servlet-class>org.alfresco.web.app.servlet.DownloadContentServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>guestDownloadContent</servlet-name>
|
||||
<servlet-class>org.alfresco.web.app.servlet.GuestDownloadContentServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>externalAccess</servlet-name>
|
||||
<servlet-class>org.alfresco.web.app.servlet.ExternalAccessServlet</servlet-class>
|
||||
@@ -230,6 +257,11 @@
|
||||
<url-pattern>/download/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>guestDownloadContent</servlet-name>
|
||||
<url-pattern>/guestDownload/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>externalAccess</servlet-name>
|
||||
<url-pattern>/navigate/*</url-pattern>
|
||||
@@ -274,7 +306,7 @@
|
||||
</welcome-file-list>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<exception-type>java.lang.Exception</exception-type>
|
||||
<location>/jsp/error.jsp</location>
|
||||
</error-page>
|
||||
|
||||
|
BIN
source/web/images/icons/edit_properties.gif
Normal file
BIN
source/web/images/icons/edit_properties.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 601 B |
BIN
source/web/images/icons/manage_workflow_task.gif
Normal file
BIN
source/web/images/icons/manage_workflow_task.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 626 B |
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
source/web/images/icons/view_properties.gif
Normal file
BIN
source/web/images/icons/view_properties.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 565 B |
BIN
source/web/images/icons/view_workflow_task.gif
Normal file
BIN
source/web/images/icons/view_workflow_task.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1023 B |
@@ -301,20 +301,20 @@
|
||||
<td colspan="2"><h:selectBooleanCheckbox value="#{AdvancedSearchBean.modifiedDateChecked}" id="chkModDate" /><span style="vertical-align:20%"><h:outputText value="#{msg.modified_date}" id="modDate" />:</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.from}" id="modDateFrom" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.modifiedDateFrom}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateModFrom" /></td>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.from}" id="modDateFrom" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.modifiedDateFrom}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateModFrom" initialiseIfNull="true" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.to}" id="modDateTo" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.modifiedDateTo}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateModTo" /></td>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.to}" id="modDateTo" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.modifiedDateTo}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateModTo" initialiseIfNull="true" /></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2"><h:selectBooleanCheckbox value="#{AdvancedSearchBean.createdDateChecked}" id="chkCreateDate" /><span style="vertical-align:20%"><h:outputText value="#{msg.created_date}" id="createDate" />:</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.from}" id="createDateFrom" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.createdDateFrom}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateCreatedFrom" /></td>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.from}" id="createDateFrom" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.createdDateFrom}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateCreatedFrom" initialiseIfNull="true" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.to}" id="createDateTo" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.createdDateTo}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateCreatedTo" /></td>
|
||||
<td style="padding-left:8px"><h:outputText value="#{msg.to}" id="createDateTo" />:</td><td><a:inputDatePicker value="#{AdvancedSearchBean.createdDateTo}" yearCount="#{DatePickerGenerator.yearCount}" startYear="#{DatePickerGenerator.startYear}" id="dateCreatedTo" initialiseIfNull="true" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="padding:4px"></div>
|
||||
|
@@ -31,11 +31,14 @@
|
||||
<a:panel id="resources-panel" label="#{msg.resources}"
|
||||
border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle">
|
||||
|
||||
<h:outputText value="#{msg.no_resources}" rendered="#{empty DialogManager.bean.resources}" />
|
||||
|
||||
<a:richList id="resources-list" viewMode="details" value="#{DialogManager.bean.resources}" var="r"
|
||||
binding="#{DialogManager.bean.packageItemsRichList}"
|
||||
styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow"
|
||||
altRowStyleClass="recordSetRowAlt" width="100%" pageSize="10"
|
||||
initialSortColumn="name" initialSortDescending="true">
|
||||
initialSortColumn="name" initialSortDescending="true"
|
||||
rendered="#{not empty DialogManager.bean.resources}">
|
||||
|
||||
<%-- Name column --%>
|
||||
<a:column id="col1" primary="true" width="200" style="padding:2px; text-align:left">
|
||||
@@ -63,7 +66,8 @@
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col3-sort" label="#{msg.path}" value="path" styleClass="header"/>
|
||||
</f:facet>
|
||||
<r:nodePath id="col3-path" value="#{r.path}" />
|
||||
<r:nodePath id="col3-path" value="#{r.path}" action="dialog:close:browse"
|
||||
actionListener="#{BrowseBean.clickSpacePath}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Created Date column --%>
|
||||
|
@@ -42,7 +42,8 @@
|
||||
titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle">
|
||||
|
||||
<r:propertySheetGrid id="task-props" value="#{WizardManager.bean.taskMetadataNode}"
|
||||
var="taskProps" columns="1" externalConfig="true" />
|
||||
var="taskProps" columns="1" externalConfig="true"
|
||||
nextButtonId="next-button" />
|
||||
</a:panel>
|
||||
|
||||
<h:outputText id="padding" styleClass="paddingRow" value=" " escape="false" />
|
||||
@@ -81,7 +82,7 @@
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col3-sort" label="#{msg.path}" value="path" styleClass="header"/>
|
||||
</f:facet>
|
||||
<r:nodePath id="col3-path" value="#{r.path}" />
|
||||
<r:nodePath id="col3-path" value="#{r.path}" disabled="true" />
|
||||
</a:column>
|
||||
|
||||
<%-- Created Date column --%>
|
||||
|
@@ -3,10 +3,13 @@
|
||||
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
|
||||
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
|
||||
|
||||
<h:outputText value="#{msg.no_tasks}" rendered="#{empty WorkflowBean.tasksCompleted}" />
|
||||
|
||||
<a:richList id="tasks-completed-list" viewMode="details" value="#{WorkflowBean.tasksCompleted}" var="r"
|
||||
styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow"
|
||||
altRowStyleClass="recordSetRowAlt" width="100%" pageSize="10"
|
||||
initialSortColumn="name" initialSortDescending="true">
|
||||
initialSortColumn="bpm:completionDate" initialSortDescending="true"
|
||||
rendered="#{not empty WorkflowBean.tasksCompleted}">
|
||||
|
||||
<%-- Primary column for details view mode --%>
|
||||
<a:column id="col1" primary="true" width="200" style="padding:2px;text-align:left">
|
||||
@@ -14,57 +17,78 @@
|
||||
<a:sortLink id="col1-sort" label="#{msg.title}" value="name" mode="case-insensitive" styleClass="header"/>
|
||||
</f:facet>
|
||||
<f:facet name="small-icon">
|
||||
<a:actionLink id="col1-act1" value="#{r.name}" image="/images/icons/completed_workflow_task.gif" showLink="false"
|
||||
<a:actionLink id="col1-act1" value="#{r['bpm:description']}" image="/images/icons/completed_workflow_task.gif" showLink="false"
|
||||
actionListener="#{DialogManager.setupParameters}" action="dialog:viewCompletedTask">
|
||||
<f:param name="id" value="#{r.id}" />
|
||||
</a:actionLink>
|
||||
</f:facet>
|
||||
<a:actionLink id="col1-act2" value="#{r.name}" actionListener="#{DialogManager.setupParameters}"
|
||||
<a:actionLink id="col1-act2" value="#{r['bpm:description']}" actionListener="#{DialogManager.setupParameters}"
|
||||
action="dialog:viewCompletedTask">
|
||||
<f:param name="id" value="#{r.id}" />
|
||||
</a:actionLink>
|
||||
</a:column>
|
||||
|
||||
|
||||
<%-- Task id column --%>
|
||||
<a:column id="col2" style="text-align:left">
|
||||
<a:column id="col2" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col2-sort" label="#{msg.id}" value="bpm:taskId" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col2-txt" value="#{r['bpm:taskId']}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Task type --%>
|
||||
<a:column id="col2a" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col2a-sort" label="#{msg.type}" value="name" mode="case-insensitive" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col2a-txt" value="#{r.name}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Source column --%>
|
||||
<a:column id="col3" style="text-align:left">
|
||||
<a:column id="col3" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col3-sort" label="#{msg.source}" value="sourceSpaceName" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col3-txt" value="#{r.sourceSpaceName}" />
|
||||
<a:actionLink id="col3-act1" value="#{r.sourceSpaceName}"
|
||||
actionListener="#{BrowseBean.clickSpace}" action="browse">
|
||||
<f:param name="id" value="#{r.sourceSpaceId}" />
|
||||
</a:actionLink>
|
||||
</a:column>
|
||||
|
||||
<%-- Created Date column --%>
|
||||
<a:column id="col4" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col4-sort" label="#{msg.created}" value="created" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col4-txt" value="#{r.created}">
|
||||
<a:convertXMLDate type="both" pattern="#{msg.date_time_pattern}" />
|
||||
</h:outputText>
|
||||
</a:column>
|
||||
|
||||
<%-- Completed date column --%>
|
||||
<a:column id="col4" style="text-align:left">
|
||||
<a:column id="col5" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col4-sort" label="#{msg.completed_on}" value="bpm:completionDate" styleClass="header"/>
|
||||
<a:sortLink id="col5-sort" label="#{msg.completed_on}" value="bpm:completionDate" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col4-txt" value="#{r['bpm:completionDate']}">
|
||||
<h:outputText id="col5-txt" value="#{r['bpm:completionDate']}">
|
||||
<a:convertXMLDate type="both" pattern="#{msg.date_pattern}" />
|
||||
</h:outputText>
|
||||
</a:column>
|
||||
|
||||
<%-- Outcome column --%>
|
||||
<a:column id="col5" style="text-align:left">
|
||||
<a:column id="col6" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col5-sort" label="#{msg.outcome}" value="outcome" styleClass="header"/>
|
||||
<a:sortLink id="col6-sort" label="#{msg.outcome}" value="outcome" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col5-txt" value="#{r.outcome}" />
|
||||
<h:outputText id="col6-txt" value="#{r.outcome}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Actions column --%>
|
||||
<a:column id="col6" actions="true" style="text-align:left">
|
||||
<a:column id="col7" actions="true" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<h:outputText id="col6-txt" value="#{msg.actions}"/>
|
||||
<h:outputText id="col7-txt" value="#{msg.actions}"/>
|
||||
</f:facet>
|
||||
<r:actions id="col6-actions" value="dashlet_completed_actions" context="#{r}" showLink="false"
|
||||
<r:actions id="col7-actions" value="dashlet_completed_actions" context="#{r}" showLink="false"
|
||||
styleClass="inlineAction" />
|
||||
</a:column>
|
||||
|
||||
|
@@ -3,76 +3,100 @@
|
||||
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
|
||||
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
|
||||
|
||||
<h:outputText value="#{msg.no_tasks}" rendered="#{empty WorkflowBean.tasksToDo}" />
|
||||
|
||||
<a:richList id="tasks-todo-list" viewMode="details" value="#{WorkflowBean.tasksToDo}" var="r"
|
||||
styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow"
|
||||
altRowStyleClass="recordSetRowAlt" width="100%" pageSize="10"
|
||||
initialSortColumn="name" initialSortDescending="true">
|
||||
|
||||
initialSortColumn="created" initialSortDescending="true"
|
||||
rendered="#{not empty WorkflowBean.tasksToDo}">
|
||||
|
||||
<%-- Primary column for details view mode --%>
|
||||
<a:column id="col1" primary="true" width="200" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col1-sort" label="#{msg.title}" value="name" mode="case-insensitive" styleClass="header"/>
|
||||
</f:facet>
|
||||
<f:facet name="small-icon">
|
||||
<a:actionLink id="col1-act1" value="#{r.name}" image="/images/icons/workflow_task.gif" showLink="false"
|
||||
<a:actionLink id="col1-act1" value="#{r['bpm:description']}" image="/images/icons/workflow_task.gif" showLink="false"
|
||||
actionListener="#{DialogManager.setupParameters}" action="dialog:manageTask">
|
||||
<f:param name="id" value="#{r.id}" />
|
||||
</a:actionLink>
|
||||
</f:facet>
|
||||
<a:actionLink id="col1-act2" value="#{r.name}" actionListener="#{DialogManager.setupParameters}"
|
||||
<a:actionLink id="col1-act2" value="#{r['bpm:description']}" actionListener="#{DialogManager.setupParameters}"
|
||||
action="dialog:manageTask">
|
||||
<f:param name="id" value="#{r.id}" />
|
||||
</a:actionLink>
|
||||
</a:column>
|
||||
|
||||
|
||||
<%-- Task id column --%>
|
||||
<a:column id="col2" style="text-align:left">
|
||||
<a:column id="col2" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col2-sort" label="#{msg.id}" value="bpm:taskId" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col2-txt" value="#{r['bpm:taskId']}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Task type --%>
|
||||
<a:column id="col2a" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col2a-sort" label="#{msg.type}" value="name" mode="case-insensitive" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col2a-txt" value="#{r.name}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Source column --%>
|
||||
<a:column id="col3" style="text-align:left">
|
||||
<a:column id="col3" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col3-sort" label="#{msg.source}" value="sourceSpaceName" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col3-txt" value="#{r.sourceSpaceName}" />
|
||||
<a:actionLink id="col3-act1" value="#{r.sourceSpaceName}"
|
||||
actionListener="#{BrowseBean.clickSpace}" action="browse">
|
||||
<f:param name="id" value="#{r.sourceSpaceId}" />
|
||||
</a:actionLink>
|
||||
</a:column>
|
||||
|
||||
<%-- Created Date column --%>
|
||||
<a:column id="col4" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col4-sort" label="#{msg.created}" value="created" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col4-txt" value="#{r.created}">
|
||||
<a:convertXMLDate type="both" pattern="#{msg.date_time_pattern}" />
|
||||
</h:outputText>
|
||||
</a:column>
|
||||
|
||||
<%-- Due date column --%>
|
||||
<a:column id="col4" style="text-align:left">
|
||||
<a:column id="col5" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col4-sort" label="#{msg.due_date}" value="bpm:startDate" styleClass="header"/>
|
||||
<a:sortLink id="col5-sort" label="#{msg.due_date}" value="bpm:dueDate" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col4-txt" value="#{r['bpm:dueDate']}">
|
||||
<h:outputText id="col5-txt" value="#{r['bpm:dueDate']}">
|
||||
<a:convertXMLDate type="both" pattern="#{msg.date_pattern}" />
|
||||
</h:outputText>
|
||||
</a:column>
|
||||
|
||||
<%-- Status column --%>
|
||||
<a:column id="col5" style="text-align:left">
|
||||
<a:column id="col6" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col5-sort" label="#{msg.status}" value="bpm:status" styleClass="header"/>
|
||||
<a:sortLink id="col6-sort" label="#{msg.status}" value="bpm:status" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col5-txt" value="#{r['bpm:status']}" />
|
||||
<h:outputText id="col6-txt" value="#{r['bpm:status']}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Priority column --%>
|
||||
<a:column id="col6" style="text-align:left">
|
||||
<a:column id="col7" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col6-sort" label="#{msg.priority}" value="bpm:priority" styleClass="header"/>
|
||||
<a:sortLink id="col7-sort" label="#{msg.priority}" value="bpm:priority" styleClass="header"/>
|
||||
</f:facet>
|
||||
<h:outputText id="col6-txt" value="#{r['bpm:priority']}" />
|
||||
<h:outputText id="col7-txt" value="#{r['bpm:priority']}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Actions column --%>
|
||||
<a:column id="col7" actions="true" style="text-align:left">
|
||||
<a:column id="col8" actions="true" style="padding:2px;text-align:left">
|
||||
<f:facet name="header">
|
||||
<h:outputText id="col7-txt" value="#{msg.actions}"/>
|
||||
<h:outputText id="col8-txt" value="#{msg.actions}"/>
|
||||
</f:facet>
|
||||
<r:actions id="col7-actions" value="dashlet_todo_actions" context="#{r}" showLink="false"
|
||||
<r:actions id="col8-actions" value="dashlet_todo_actions" context="#{r}" showLink="false"
|
||||
styleClass="inlineAction" />
|
||||
</a:column>
|
||||
|
||||
|
@@ -31,11 +31,14 @@
|
||||
<a:panel id="resources-panel" label="#{msg.resources}"
|
||||
border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle">
|
||||
|
||||
<h:outputText value="#{msg.no_resources}" rendered="#{empty DialogManager.bean.resources}" />
|
||||
|
||||
<a:richList id="resources-list" viewMode="details" value="#{DialogManager.bean.resources}" var="r"
|
||||
binding="#{DialogManager.bean.packageItemsRichList}"
|
||||
styleClass="recordSet" headerStyleClass="recordSetHeader" rowStyleClass="recordSetRow"
|
||||
altRowStyleClass="recordSetRowAlt" width="100%" pageSize="10"
|
||||
initialSortColumn="name" initialSortDescending="true">
|
||||
initialSortColumn="name" initialSortDescending="true"
|
||||
rendered="#{not empty DialogManager.bean.resources}">
|
||||
|
||||
<%-- Name column --%>
|
||||
<a:column id="col1" primary="true" width="200" style="padding:2px; text-align:left">
|
||||
@@ -63,7 +66,8 @@
|
||||
<f:facet name="header">
|
||||
<a:sortLink id="col3-sort" label="#{msg.path}" value="path" styleClass="header"/>
|
||||
</f:facet>
|
||||
<r:nodePath id="col3-path" value="#{r.path}" />
|
||||
<r:nodePath id="col3-path" value="#{r.path}" action="dialog:close:browse"
|
||||
actionListener="#{BrowseBean.clickSpacePath}" />
|
||||
</a:column>
|
||||
|
||||
<%-- Created Date column --%>
|
||||
|
Reference in New Issue
Block a user