. Email Space Users functionality

- New action "Email Space Users" available on the details page for a space
 - Users/Groups explicity invited to the space are shown for emailing
 - Email Space Users UI with new JSF component for hiearchical selection of users/groups
 - Template based email to selected users/groups
. Refactored beans using template based email into a helper bean reusable by other classes
. Renamed the old template based Dashboard View (on doc/space details screens) to Custom View

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3610 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2006-08-25 13:30:35 +00:00
parent 5257546c9c
commit dbc6fa1abf
24 changed files with 1379 additions and 244 deletions

View File

@@ -514,8 +514,7 @@ modify_props_of=Modify Properties of
modify_space_properties=Modify Space Properties
modify_content_properties=Modify Content Properties
preview=Preview in Template
dashboard_view=Dashboard View
dashboard=Dashboard
custom_view=Custom View
view_links=Links
not_inline_editable=This document is not inline editable.
allow_inline_editing=Allow Inline Editing
@@ -546,9 +545,9 @@ success_unlock=Successfully unlocked the document.
inherit_permissions=Inherit Parent Space Permissions
success_inherit_permissions=Successfully changed Inherit Parent Permissions to 'Yes'
success_not_inherit_permissions=Successfully changed Inherit Parent Permissions to 'No'
apply_dashboard=Apply Dashboard
apply_dashboard_info=Select a template to be applied to the Space as a Dashboard view.
apply_dashboard_doc_info=Select a template to be applied to the Document as a Dashboard view.
apply_template=Apply Template
apply_dashboard_info=Select a template to be applied to the Space as a Custom view.
apply_dashboard_doc_info=Select a template to be applied to the Document as a Custom view.
apply_rss_feed=Apply RSS Feed Template
apply_rss_feed_info=Select a template to be applied to the Space as an RSS feed.
apply_rss_feed_warning1=This Space must be visible to the Guest user for the RSS feed to be publically viewable, you can Invite the Guest user using the
@@ -925,6 +924,10 @@ delete_op_files=Only the files within this space.
delete_op_folders=Only the folders within this space.
delete_op_contents=Files and folders within this space.
# Email users dialog
email_space_users=Email Space users
email_space_users_desc=Send an email to the users and groups assigned to this space.
# Workflow messages
start_workflow=Start Workflow
start_workflow_wizard=Start New Workflow Wizard

View File

@@ -372,6 +372,17 @@
</params>
</action>
<!-- Email Space Users -->
<action id="email_space_users">
<label-id>email_space_users</label-id>
<image>/images/icons/email_users.gif</image>
<action>dialog:emailSpaceUsers</action>
<action-listener>#{BrowseBean.setupSpaceAction}</action-listener>
<params>
<param name="id">#{actionContext.id}</param>
</params>
</action>
<!-- Manage Deleted Items -->
<action id="manage_deleted_items">
<evaluator>org.alfresco.web.action.evaluator.ShortcutNodeEvaluator</evaluator>
@@ -601,6 +612,7 @@
<action idref="take_ownership_space" />
<action idref="manage_space_users" />
<action idref="manage_space_rules" />
<action idref="email_space_users" />
<action idref="preview_space" />
<action idref="run_action" />
</action-group>

View File

@@ -81,6 +81,15 @@
<dialog name="createDiscussion" page="/jsp/forums/create-topic-dialog.jsp" managed-bean="CreateDiscussionDialog"
icon="/images/icons/create_topic_large.gif" title-id="create_topic"
description-id="create_topic_description" error-message-id="error_create_topic_dialog" />
<!-- -->
<!-- Other Dialogs -->
<!-- Definition of the create forums dialog -->
<dialog name="emailSpaceUsers" page="/jsp/users/email-space-users.jsp" managed-bean="EmailSpaceUsersDialog"
icon="/images/icons/email_users_large.gif" title-id="email_space_users"
description-id="email_space_users_desc" />
</dialogs>
</config>

View File

@@ -0,0 +1,268 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.text.MessageFormat;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.repo.component.template.DefaultModelHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
/**
* @author Kevin Roast
*/
public class TemplateMailHelperBean
{
private static Log logger = LogFactory.getLog(TemplateMailHelperBean.class);
/** JavaMailSender bean reference */
protected JavaMailSender mailSender;
/** NodeService bean reference */
protected NodeService nodeService;
/** dialog state */
private String subject = null;
private String body = null;
private String automaticText = null;
private String template = null;
private String usingTemplate = null;
private String finalBody;
/**
* @param mailSender The JavaMailSender to set.
*/
public void setMailSender(JavaMailSender mailSender)
{
this.mailSender = mailSender;
}
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Initialises the bean
*/
public TemplateMailHelperBean()
{
subject = "";
body = "";
automaticText = "";
template = null;
usingTemplate = null;
}
/**
* Send an email notification to the specified User authority
*
* @param person Person node representing the user
* @param node Node they are invited too
* @param from From text message
* @param roleText The role display label for the user invite notification
*/
public void notifyUser(NodeRef person, NodeRef node, final String from, String roleText)
{
final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL);
if (to != null && to.length() != 0)
{
String body = this.body;
if (this.usingTemplate != null)
{
FacesContext fc = FacesContext.getCurrentInstance();
// use template service to format the email
NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate);
ServiceRegistry services = Repository.getServiceRegistry(fc);
Map<String, Object> model = DefaultModelHelper.buildDefaultModel(
services, Application.getCurrentUser(fc), templateRef);
model.put("role", roleText);
model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null));
body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model);
}
this.finalBody = body;
MimeMessagePreparator mailPreparer = new MimeMessagePreparator()
{
public void prepare(MimeMessage mimeMessage) throws MessagingException
{
MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
message.setTo(to);
message.setSubject(subject);
message.setText(finalBody);
message.setFrom(from);
}
};
if (logger.isDebugEnabled())
logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body);
try
{
// Send the message
this.mailSender.send(mailPreparer);
}
catch (Throwable e)
{
// don't stop the action but let admins know email is not getting sent
logger.error("Failed to send email to " + to, e);
}
}
}
/**
* Action handler called to insert a template as the email body
*/
public void insertTemplate(ActionEvent event)
{
if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false)
{
// get the content of the template so the user can get a basic preview of it
try
{
NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template);
ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService();
ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT);
if (reader != null && reader.exists())
{
this.body = reader.getContentString();
this.usingTemplate = this.template;
}
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
}
}
}
/**
* Action handler called to discard the template from the email body
*/
public void discardTemplate(ActionEvent event)
{
this.body = this.automaticText;
usingTemplate = null;
}
/**
* @return Returns the email body text.
*/
public String getBody()
{
return this.body;
}
/**
* @param body The email body text to set.
*/
public void setBody(String body)
{
this.body = body;
}
/**
* @return Returns the email subject text.
*/
public String getSubject()
{
return this.subject;
}
/**
* @param subject The email subject text to set.
*/
public void setSubject(String subject)
{
this.subject = subject;
}
/**
* @return Returns the automatic text.
*/
public String getAutomaticText()
{
return this.automaticText;
}
/**
* @param automaticText The automatic text to set.
*/
public void setAutomaticText(String automaticText)
{
this.automaticText = automaticText;
}
/**
* @return Returns the email template Id
*/
public String getTemplate()
{
return this.template;
}
/**
* @param template The email template to set.
*/
public void setTemplate(String template)
{
this.template = template;
}
/**
* @return Returns if a template has been inserted by a user for email body.
*/
public String getUsingTemplate()
{
return this.usingTemplate;
}
/**
* @param usingTemplate Template that has been inserted by a user for the email body.
*/
public void setUsingTemplate(String usingTemplate)
{
this.usingTemplate = usingTemplate;
}
}

View File

@@ -0,0 +1,497 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.users;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.TemplateMailHelperBean;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.repo.WebResources;
import org.alfresco.web.ui.repo.component.UIUserGroupPicker;
import org.alfresco.web.ui.repo.component.UIUserGroupPicker.PickerEvent;
import org.springframework.mail.javamail.JavaMailSender;
/**
* Dialog bean managing the state for the Email Space Users page. Calculates the user/groups
* that are invited to a space and builds the data structures needed to display and modify
* the list in the web-client UI. Notifies the selected user/groups with a templatable email.
*
* @author Kevin Roast
*/
public class EmailSpaceUsersDialog extends BaseDialogBean implements IContextListener
{
private static final String PROP_DUPLICATE = "duplicate";
private static final String PROP_PARENT = "parent";
private static final String PROP_ID = "id";
private static final String PROP_ISGROUP = "isGroup";
private static final String PROP_ICON = "icon";
private static final String PROP_FULLNAME = "fullName";
private static final String PROP_ROLES = "roles";
private static final String PROP_EXPANDED = "expanded";
private static final String PROP_SELECTED = "selected";
private static final String PROP_USERNAME = "userName";
/** Injected Bean references */
protected PermissionService permissionService;
protected PersonService personService;
protected AuthorityService authorityService;
protected JavaMailSender mailSender;
/** Helper providing template based mailing facilities */
protected TemplateMailHelperBean mailHelper;
/** List of user/group property map/node instances */
private List<Map> usersGroups = null;
/** Quick lookup table of authority to user/group instance */
private Map<String, Map> userGroupLookup = new HashMap<String, Map>();
/**
* Default constructor
*/
public EmailSpaceUsersDialog()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
}
/**
* Setup the dialog
*/
public void init(Map<String, String> parameters)
{
super.init(parameters);
mailHelper = new TemplateMailHelperBean();
mailHelper.setMailSender(mailSender);
mailHelper.setNodeService(nodeService);
}
/**
* @see org.alfresco.web.bean.dialog.BaseDialogBean#finishImpl(javax.faces.context.FacesContext, java.lang.String)
*/
@Override
protected String finishImpl(FacesContext context, String outcome) throws Exception
{
// get the space ref this mail applies to
NodeRef spaceRef = getSpace().getNodeRef();
// calculate the 'from' email address
User user = Application.getCurrentUser(context);
String from = (String)this.nodeService.getProperty(user.getPerson(), ContentModel.PROP_EMAIL);
if (from == null || from.length() == 0)
{
// if the user does not have an email address get the default one from the config service
from = Application.getClientConfig(context).getFromEmailAddress();
}
Set<String> mailedAuthorities = new HashSet<String>(usersGroups.size());
// walk the list of users/groups to notify
for (Map node : usersGroups)
{
String authority = (String)node.get(PROP_USERNAME);
boolean selected = (Boolean)node.get(PROP_SELECTED);
// if User, email then, else if Group get all members and email them
AuthorityType authType = AuthorityType.getAuthorityType(authority);
if (authType.equals(AuthorityType.USER))
{
if (selected == true && this.personService.personExists(authority))
{
if (mailedAuthorities.contains(authority) == false)
{
this.mailHelper.notifyUser(
this.personService.getPerson(authority), spaceRef, from, (String)node.get(PROP_ROLES));
mailedAuthorities.add(authority);
}
}
}
else if (authType.equals(AuthorityType.GROUP))
{
// is the group expanded? if so we'll deal with the child authorities instead
boolean expanded = (Boolean)node.get(PROP_EXPANDED);
if (expanded == false && selected == true)
{
// notify all members of the group
Set<String> users = this.authorityService.getContainedAuthorities(AuthorityType.USER, authority, false);
for (String userAuth : users)
{
if (this.personService.personExists(userAuth) == true)
{
if (mailedAuthorities.contains(userAuth) == false)
{
this.mailHelper.notifyUser(
this.personService.getPerson(userAuth), spaceRef, from, (String)node.get(PROP_ROLES));
mailedAuthorities.add(userAuth);
}
}
}
}
}
}
return outcome;
}
// ------------------------------------------------------------------------------
// IContextListener implementation
/**
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
*/
public void contextUpdated()
{
this.usersGroups = null;
this.userGroupLookup = new HashMap<String, Map>();
}
// ------------------------------------------------------------------------------
// Bean Getters and Setters
/**
* @param permissionService The PermissionService to set
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @param permissionService The PersonService to set
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @param mailSender The JavaMailSender to set.
*/
public void setMailSender(JavaMailSender mailSender)
{
this.mailSender = mailSender;
}
/**
* @param authorityService The AuthorityService to set.
*/
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
/**
* @return The space to email users for
*/
public Node getSpace()
{
return this.browseBean.getActionSpace();
}
/**
* Return the List of objects representing the Users and Groups invited to this space.
* The picker is then responsible for rendering a view to represent those users and groups
* which allows the users to select and deselect users and groups, also to expand groups
* to show sub-groups and users.
*
* @return List of Map objects representing the users/groups assigned to the current space
*/
public List<Map> getUsersGroups()
{
if (this.usersGroups == null)
{
FacesContext context = FacesContext.getCurrentInstance();
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// Return all the permissions set against the current node
// for any authentication instance (user/group).
// Then combine them into a single list for each authentication found.
Map<String, List<String>> permissionMap = new HashMap<String, List<String>>(8, 1.0f);
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(getSpace().getNodeRef());
for (AccessPermission permission : permissions)
{
// we are only interested in Allow and not groups/owner etc.
if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
(permission.getAuthorityType() == AuthorityType.USER ||
permission.getAuthorityType() == AuthorityType.GROUP ||
permission.getAuthorityType() == AuthorityType.GUEST ||
permission.getAuthorityType() == AuthorityType.EVERYONE))
{
String authority = permission.getAuthority();
List<String> userPermissions = permissionMap.get(authority);
if (userPermissions == null)
{
// create for first time
userPermissions = new ArrayList<String>(4);
permissionMap.put(authority, userPermissions);
}
// add the permission name for this authority
userPermissions.add(permission.getPermission());
}
}
// create the structure as a linked list for fast insert/removal of items
this.usersGroups = new LinkedList<Map>();
// for each authentication (username/group key) found we get the Person
// node represented by it and use that for our list databinding object
for (String authority : permissionMap.keySet())
{
Map node = buildAuthorityMap(authority, UserMembersBean.roleListToString(context, permissionMap.get(authority)));
if (node != null)
{
this.usersGroups.add(node);
}
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) );
this.usersGroups = Collections.<Map>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_GENERIC), err.getMessage()), err );
this.usersGroups = Collections.<Map>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
return this.usersGroups;
}
/**
* Build a Map representing a user/group with a set of useful property values required
* by the UIUserGroupPicker UI component.
*
* @param authority User/Group authority
* @param roles Role text for the authority
*
* @return Map
*/
private Map buildAuthorityMap(String authority, String roles)
{
Map node = null;
if (AuthorityType.getAuthorityType(authority) == AuthorityType.GUEST ||
this.personService.personExists(authority))
{
NodeRef nodeRef = this.personService.getPerson(authority);
if (nodeRef != null)
{
// create our Node representation
node = new MapNode(nodeRef);
// set data binding properties
// this will also force initialisation of the props now during the UserTransaction
// it is much better for performance to do this now rather than during page bind
Map<String, Object> props = ((MapNode)node).getProperties();
props.put(PROP_FULLNAME, ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
props.put(PROP_ICON, WebResources.IMAGE_PERSON);
props.put(PROP_ISGROUP, false);
}
}
else if (AuthorityType.getAuthorityType(authority) == AuthorityType.GROUP)
{
// need a map (dummy node) to represent props for this Group Authority
node = new HashMap<String, Object>(8, 1.0f);
if (authority.startsWith(PermissionService.GROUP_PREFIX) == true)
{
node.put(PROP_FULLNAME, authority.substring(PermissionService.GROUP_PREFIX.length()));
}
else
{
node.put(PROP_FULLNAME, authority);
}
node.put(PROP_USERNAME, authority);
node.put(PROP_ID, authority);
node.put(PROP_ICON, WebResources.IMAGE_GROUP);
node.put(PROP_ISGROUP, true);
node.put(PROP_EXPANDED, false);
}
if (node != null)
{
// add the common properties
node.put(PROP_ROLES, roles);
node.put(PROP_PARENT, null);
if (this.userGroupLookup.get(authority) != null)
{
// this authority already exists in the list somewhere else - mark as duplicate
node.put(PROP_DUPLICATE, true);
node.put(PROP_SELECTED, false);
}
else
{
// add to table for the first time, not a duplicate
this.userGroupLookup.put(authority, node);
node.put(PROP_DUPLICATE, false);
node.put(PROP_SELECTED, true);
}
}
return node;
}
/**
* @return TemplateMailHelperBean instance for this wizard
*/
public TemplateMailHelperBean getMailHelper()
{
return this.mailHelper;
}
// ------------------------------------------------------------------------------
// Action Event Listeners
/**
* Action handler for a user/group selector event
*/
public void userGroupSelectorAction(ActionEvent event)
{
if (event instanceof PickerEvent)
{
PickerEvent pickerEvent = (PickerEvent)event;
// find the user/group this event represents
Map userGroup = null;
int index = 0;
for (; index<this.usersGroups.size(); index++)
{
if (pickerEvent.Authority.equals(this.usersGroups.get(index).get(PROP_ID)))
{
userGroup = this.usersGroups.get(index);
break;
}
}
if (userGroup != null)
{
switch (pickerEvent.Action)
{
// expand/collapse events only applicable for a Group
case UIUserGroupPicker.ACTION_EXPANDCOLLAPSE:
boolean expanded = (Boolean)userGroup.get(PROP_EXPANDED);
userGroup.put(PROP_EXPANDED, !expanded);
if (expanded == false)
{
// expand the list for this group by adding the immediate child authorities
boolean selected = (Boolean)userGroup.get(PROP_SELECTED);
Set<String> authorities = authorityService.getContainedAuthorities(
null, pickerEvent.Authority, true);
for (String authority : authorities)
{
Map node = buildAuthorityMap(authority, (String)userGroup.get(PROP_ROLES));
if (node != null)
{
node.put(PROP_PARENT, userGroup);
node.put(PROP_SELECTED, selected);
this.usersGroups.add(++index, node);
}
}
}
else
{
// remove the children for the group
for (index++; index<this.usersGroups.size(); /**/)
{
Map node = this.usersGroups.get(index);
Map parent = (Map)node.get(PROP_PARENT);
// only remove those Groups that have this group as the parent
// they are added sequentially - so we know when to stop removing
boolean foundParent = false;
while (parent != null && foundParent == false)
{
// search up the parent hierarchy
if (parent == userGroup)
{
foundParent = true;
}
parent = (Map)parent.get(PROP_PARENT);
}
if (foundParent == true)
{
// handle duplicates - only remove the first from the lookup table
if (((Boolean)node.get(PROP_DUPLICATE)) == false)
{
this.userGroupLookup.remove((String)node.get(PROP_USERNAME));
}
this.usersGroups.remove(index);
}
else
{
// need to increment loop counter if did not remove a value from the list
index++;
}
}
}
break;
case UIUserGroupPicker.ACTION_SELECT:
boolean selected = (Boolean)userGroup.get(PROP_SELECTED);
userGroup.put(PROP_SELECTED, !selected);
break;
}
}
}
}
}

View File

@@ -287,37 +287,34 @@ public abstract class UserMembersBean implements IContextListener
tx.begin();
// Return all the permissions set against the current node
// for any authentication instance (user).
// for any authentication instance (user/group).
// Then combine them into a single list for each authentication found.
Map<String, List<String>> permissionMap = new HashMap<String, List<String>>(13, 1.0f);
Map<String, List<String>> permissionMap = new HashMap<String, List<String>>(8, 1.0f);
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(getNode().getNodeRef());
if (permissions != null)
for (AccessPermission permission : permissions)
{
for (AccessPermission permission : permissions)
// we are only interested in Allow and not groups/owner etc.
if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
(permission.getAuthorityType() == AuthorityType.USER ||
permission.getAuthorityType() == AuthorityType.GROUP ||
permission.getAuthorityType() == AuthorityType.GUEST ||
permission.getAuthorityType() == AuthorityType.EVERYONE))
{
// we are only interested in Allow and not groups/owner etc.
if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
(permission.getAuthorityType() == AuthorityType.USER ||
permission.getAuthorityType() == AuthorityType.GROUP ||
permission.getAuthorityType() == AuthorityType.GUEST ||
permission.getAuthorityType() == AuthorityType.EVERYONE))
{
String authority = permission.getAuthority();
String authority = permission.getAuthority();
List<String> userPermissions = permissionMap.get(authority);
if (userPermissions == null)
{
// create for first time
userPermissions = new ArrayList<String>(4);
permissionMap.put(authority, userPermissions);
}
// add the permission name for this authority
userPermissions.add(permission.getPermission());
List<String> userPermissions = permissionMap.get(authority);
if (userPermissions == null)
{
// create for first time
userPermissions = new ArrayList<String>(4);
permissionMap.put(authority, userPermissions);
}
// add the permission name for this authority
userPermissions.add(permission.getPermission());
}
}
// for each authentication (username key) found we get the Person
// for each authentication (username/group key) found we get the Person
// node represented by it and use that for our list databinding object
personNodes = new ArrayList<Map>(permissionMap.size());
for (String authority : permissionMap.keySet())
@@ -337,7 +334,7 @@ public abstract class UserMembersBean implements IContextListener
// it is much better for performance to do this now rather than during page bind
Map<String, Object> props = node.getProperties();
props.put("fullName", ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
props.put("roles", listToString(context, permissionMap.get(authority)));
props.put("roles", roleListToString(context, permissionMap.get(authority)));
props.put("icon", WebResources.IMAGE_PERSON);
personNodes.add(node);
@@ -357,7 +354,7 @@ public abstract class UserMembersBean implements IContextListener
}
node.put("userName", authority);
node.put("id", authority);
node.put("roles", listToString(context, permissionMap.get(authority)));
node.put("roles", roleListToString(context, permissionMap.get(authority)));
node.put("icon", WebResources.IMAGE_GROUP);
personNodes.add(node);
}
@@ -369,7 +366,7 @@ public abstract class UserMembersBean implements IContextListener
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_NODEREF), new Object[] {"root"}) );
context, Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) );
personNodes = Collections.<Map>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
@@ -384,7 +381,16 @@ public abstract class UserMembersBean implements IContextListener
return personNodes;
}
private static String listToString(FacesContext context, List<String> list)
/**
* Convert a list of user Roles to a comma separated string list. Each individual role
* will be looked up in message bundle to convert to a human readable string value.
*
* @param context FacesContext
* @param list List of Role names
*
* @return Comma separated string of human readable roles
*/
public static String roleListToString(FacesContext context, List<String> list)
{
StringBuilder buf = new StringBuilder();

View File

@@ -20,7 +20,6 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
@@ -30,16 +29,10 @@ import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
@@ -47,20 +40,16 @@ import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.TemplateSupportBean;
import org.alfresco.web.bean.TemplateMailHelperBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.SortableSelectItem;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIGenericPicker;
import org.alfresco.web.ui.repo.component.template.DefaultModelHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
/**
* @author Kevin Roast
@@ -96,6 +85,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
/** personService bean reference */
protected PersonService personService;
/** Helper providing template based mailing facilities */
protected TemplateMailHelperBean mailHelper;
/** datamodel for table of roles for users */
private DataModel userRolesDataModel = null;
@@ -104,12 +96,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
/** dialog state */
private String notify = NOTIFY_YES;
private String subject = null;
private String body = null;
private String automaticText = null;
private String template = null;
private String usingTemplate = null;
private String finalBody;
/**
* @return a cached list of available permissions for the type being dealt with
@@ -175,11 +161,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
notify = NOTIFY_YES;
userGroupRoles = new ArrayList<UserGroupRole>(8);
subject = "";
body = "";
automaticText = "";
template = null;
usingTemplate = null;
mailHelper = new TemplateMailHelperBean();
mailHelper.setMailSender(mailSender);
mailHelper.setNodeService(nodeService);
}
/**
@@ -239,7 +223,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
{
if (this.personService.personExists(authority) == true)
{
notifyUser(this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole());
this.mailHelper.notifyUser(
this.personService.getPerson(authority), nodeRef, from, userGroupRole.getRole());
}
}
else if (authType.equals(AuthorityType.GROUP))
@@ -250,7 +235,8 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
{
if (this.personService.personExists(userAuth) == true)
{
notifyUser(this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole());
this.mailHelper.notifyUser(
this.personService.getPerson(userAuth), nodeRef, from, userGroupRole.getRole());
}
}
}
@@ -274,65 +260,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
return outcome;
}
/**
* Send an email notification to the specified User authority
*
* @param person Person node representing the user
* @param node Node they are invited too
* @param from From text message
* @param roleText The role display label for the user invite notification
*/
private void notifyUser(NodeRef person, NodeRef node, final String from, String roleText)
{
final String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL);
if (to != null && to.length() != 0)
{
String body = this.body;
if (this.usingTemplate != null)
{
FacesContext fc = FacesContext.getCurrentInstance();
// use template service to format the email
NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.usingTemplate);
ServiceRegistry services = Repository.getServiceRegistry(fc);
Map<String, Object> model = DefaultModelHelper.buildDefaultModel(
services, Application.getCurrentUser(fc), templateRef);
model.put("role", roleText);
model.put("space", new TemplateNode(node, Repository.getServiceRegistry(fc), null));
body = services.getTemplateService().processTemplate("freemarker", templateRef.toString(), model);
}
this.finalBody = body;
MimeMessagePreparator mailPreparer = new MimeMessagePreparator()
{
public void prepare(MimeMessage mimeMessage) throws MessagingException
{
MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
message.setTo(to);
message.setSubject(subject);
message.setText(finalBody);
message.setFrom(from);
}
};
if (logger.isDebugEnabled())
logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body);
try
{
// Send the message
this.mailSender.send(mailPreparer);
}
catch (Throwable e)
{
// don't stop the action but let admins know email is not getting sent
logger.error("Failed to send email to " + to, e);
}
}
}
/**
* Returns the properties for current user-roles JSF DataModel
*
@@ -552,43 +479,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
return roles;
}
/**
* Action handler called to insert a template as the email body
*/
public void insertTemplate(ActionEvent event)
{
if (this.template != null && this.template.equals(TemplateSupportBean.NO_SELECTION) == false)
{
// get the content of the template so the user can get a basic preview of it
try
{
NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template);
ContentService cs = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService();
ContentReader reader = cs.getReader(templateRef, ContentModel.PROP_CONTENT);
if (reader != null && reader.exists())
{
this.body = reader.getContentString();
this.usingTemplate = this.template;
}
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
}
}
}
/**
* Action handler called to discard the template from the email body
*/
public void discardTemplate(ActionEvent event)
{
this.body = this.automaticText;
usingTemplate = null;
}
/**
* @return Returns the notify listbox selection.
*/
@@ -605,70 +495,6 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
this.notify = notify;
}
/**
* @return Returns the email body text.
*/
public String getBody()
{
return this.body;
}
/**
* @param body The email body text to set.
*/
public void setBody(String body)
{
this.body = body;
}
/**
* @return Returns the email subject text.
*/
public String getSubject()
{
return this.subject;
}
/**
* @param subject The email subject text to set.
*/
public void setSubject(String subject)
{
this.subject = subject;
}
/**
* @return Returns the email template Id
*/
public String getTemplate()
{
return this.template;
}
/**
* @param template The email template to set.
*/
public void setTemplate(String template)
{
this.template = template;
}
/**
* @return Returns if a template has been inserted by a user for email body.
*/
public String getUsingTemplate()
{
return this.usingTemplate;
}
/**
* @param usingTemplate Template that has been inserted by a user for the email body.
*/
public void setUsingTemplate(String usingTemplate)
{
this.usingTemplate = usingTemplate;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
@@ -771,7 +597,7 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
personName}) );
// default the subject line to an informative message
this.subject = buf.toString();
this.mailHelper.setSubject(buf.toString());
// add the rest of the automatic body text
buf.append("\r\n\r\n");
@@ -790,10 +616,9 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
buf.append(roleText);
this.automaticText = buf.toString();
// default the body content to this text
this.body = this.automaticText;
// set the body content and default text to this text
this.mailHelper.setAutomaticText(buf.toString());
this.mailHelper.setBody(this.mailHelper.getAutomaticText());
}
return outcome;
@@ -827,6 +652,14 @@ public abstract class InviteUsersWizard extends AbstractWizardBean
return outcome;
}
/**
* @return TemplateMailHelperBean instance for this wizard
*/
public TemplateMailHelperBean getMailHelper()
{
return this.mailHelper;
}
/**
* Simple wrapper class to represent a user/group and a role combination
*/

View File

@@ -0,0 +1,240 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.repo.component;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.component.NamingContainer;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.WebResources;
/**
* Seld rendering component tied to the EmailSpaceUsersDialog bean. Renders a hierarchy of
* user/group authorities. Each authority can be (de)selected and groups can be expanded/collapsed
* to display and select from the child authorities in the group. Nested groups are supported.
*
* @author Kevin Roast
*/
public class UIUserGroupPicker extends UICommand
{
/** action ids */
public final static int ACTION_NONE = -1;
public final static int ACTION_EXPANDCOLLAPSE = 0;
public final static int ACTION_SELECT = 1;
private static String SELECTED_AUTHORITY = "_check";
// ------------------------------------------------------------------------------
// Component implementation
/**
* Default constructor
*/
public UIUserGroupPicker()
{
setRendererType(null);
}
/**
* @see javax.faces.component.UIComponent#getFamily()
*/
public String getFamily()
{
return "org.alfresco.faces.UserGroupPicker";
}
/**
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
*/
public void decode(FacesContext context)
{
Map requestMap = context.getExternalContext().getRequestParameterMap();
Map valuesMap = context.getExternalContext().getRequestParameterValuesMap();
String fieldId = getHiddenFieldName(context);
String value = (String)requestMap.get(fieldId);
if (value != null && value.length() != 0)
{
// decode the values - we are expecting an action identifier and an authority name
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
int action = Integer.parseInt(value.substring(0, sepIndex));
String authority = value.substring(sepIndex + 1);
// queue an event
PickerEvent event = new PickerEvent(this, action, authority);
queueEvent(event);
}
}
/**
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
*/
public void encodeBegin(FacesContext context) throws IOException
{
if (isRendered() == false)
{
return;
}
ResponseWriter out = context.getResponseWriter();
ResourceBundle bundle = Application.getBundle(context);
String clientId = getClientId(context);
// start outer table
out.write("<table width=100% border=0 cellspacing=0 cellpadding=0 class='userGroupPickerList'>");
// get the data that represents the users/groups to display
List<Map> userGroups = (List<Map>)getValue();
if (userGroups != null)
{
for (Map authority : userGroups)
{
String authorityId = (String)authority.get("id");
out.write("<tr><td width=100%><table width=100% border=0 cellspacing=3 cellpadding=0><tr>");
// walk parent hierarchy to calculate width of this cell
int width = 16;
Map parent = (Map)authority.get("parent");
while (parent != null)
{
width += 16;
parent = (Map)parent.get("parent");
}
out.write("<td width=");
out.write(Integer.toString(width));
out.write(" align=right>");
// output expanded/collapsed icon if authority is a group
boolean expanded = false;
boolean isGroup = (Boolean)authority.get("isGroup");
if (isGroup)
{
// either output the expanded or collapsed selectable widget
expanded = (Boolean)authority.get("expanded");
String image = expanded ? WebResources.IMAGE_EXPANDED : WebResources.IMAGE_COLLAPSED;
out.write(Utils.buildImageTag(context, image, 11, 11, "",
generateFormSubmit(context, ACTION_EXPANDCOLLAPSE, authorityId)));
}
out.write("</td><td width=16>");
// output selected checkbox if not expanded and not a duplicate
boolean duplicate = (Boolean)authority.get("duplicate");
if (duplicate == false && (isGroup == false || expanded == false))
{
boolean selected = (Boolean)authority.get("selected");
out.write("<input type='checkbox' value='' name='");
out.write(clientId + NamingContainer.SEPARATOR_CHAR + SELECTED_AUTHORITY);
out.write("' onclick=\"");
out.write(generateFormSubmit(context, ACTION_SELECT, authorityId));
out.write('"');
if (selected)
{
out.write(" CHECKED");
}
out.write('>');
}
out.write("</td><td width=16>");
// output icon
out.write(Utils.buildImageTag(context, (String)authority.get("icon"), 16, 16, ""));
out.write("</td><td>");
// output textual information
if (duplicate)
{
out.write("<span style='color:#93a8b2'>");
}
out.write((String)authority.get("fullName"));
out.write(" (");
out.write((String)authority.get("roles"));
out.write(")");
if (duplicate)
{
out.write("</span>");
}
out.write("</td>");
out.write("</tr></table></td></tr>");
}
}
out.write("</table>");
}
// ------------------------------------------------------------------------------
// Private helpers
/**
* We use a hidden field per picker instance on the page.
*
* @return hidden field name
*/
private String getHiddenFieldName(FacesContext context)
{
return getClientId(context);
}
/**
* Generate FORM submit JavaScript for the specified action
*
* @param context FacesContext
* @param action Action index
* @param authority Authority Id of the action source
*
* @return FORM submit JavaScript
*/
private String generateFormSubmit(FacesContext context, int action, String authority)
{
return Utils.generateFormSubmit(context, this, getHiddenFieldName(context),
Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + authority);
}
// ------------------------------------------------------------------------------
// Inner classes
/**
* Class representing the an action relevant to the User Group picker component.
*/
public static class PickerEvent extends ActionEvent
{
public PickerEvent(UIComponent component, int action, String authority)
{
super(component);
Action = action;
Authority = authority;
}
public String Authority;
public int Action;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.repo.tag;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import org.alfresco.web.ui.common.tag.HtmlComponentTag;
/**
* @author Kevin Roast
*/
public class UserGroupPickerTag extends HtmlComponentTag
{
/**
* @see javax.faces.webapp.UIComponentTag#getComponentType()
*/
public String getComponentType()
{
return "org.alfresco.faces.UserGroupPicker";
}
/**
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/
public String getRendererType()
{
return null;
}
/**
* @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
*/
protected void setProperties(UIComponent component)
{
super.setProperties(component);
setStringProperty(component, "value", this.value);
setActionListenerProperty((UICommand)component, this.actionListener);
}
/**
* @see org.alfresco.web.ui.common.tag.HtmlComponentTag#release()
*/
public void release()
{
super.release();
this.value = null;
}
/**
* Set the value (binding to the list of user/group data)
*
* @param value the value
*/
public void setValue(String value)
{
this.value = value;
}
/**
* Set the actionListener
*
* @param actionListener the actionListener
*/
public void setActionListener(String actionListener)
{
this.actionListener = actionListener;
}
/** the value (binding to the list of user/group data) */
private String value;
/** the actionListener */
private String actionListener;
}

View File

@@ -1881,6 +1881,40 @@
</managed-property>
</managed-bean>
<managed-bean>
<description>
The bean that backs up the Email Space Users Dialog
</description>
<managed-bean-name>EmailSpaceUsersDialog</managed-bean-name>
<managed-bean-class>org.alfresco.web.bean.users.EmailSpaceUsersDialog</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>browseBean</property-name>
<value>#{BrowseBean}</value>
</managed-property>
<managed-property>
<property-name>nodeService</property-name>
<value>#{NodeService}</value>
</managed-property>
<managed-property>
<property-name>permissionService</property-name>
<value>#{PermissionService}</value>
</managed-property>
<managed-property>
<property-name>personService</property-name>
<value>#{PersonService}</value>
</managed-property>
<managed-property>
<property-name>authorityService</property-name>
<value>#{AuthorityService}</value>
</managed-property>
<managed-property>
<property-name>mailSender</property-name>
<value>#{mailService}</value>
</managed-property>
</managed-bean>
<!-- ==================== COMPONENT GENERATOR BEANS ==================== -->
<managed-bean>
<description>
@@ -2035,6 +2069,7 @@
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<!-- ==================== AJAX BEANS ==================== -->
<managed-bean>

View File

@@ -139,6 +139,11 @@
<component-class>org.alfresco.web.ui.repo.component.UIDialogButtons</component-class>
</component>
<component>
<component-type>org.alfresco.faces.UserGroupPicker</component-type>
<component-class>org.alfresco.web.ui.repo.component.UIUserGroupPicker</component-class>
</component>
<!-- ==================== CONVERTERS ==================== -->
<component>

View File

@@ -1560,4 +1560,58 @@
</attribute>
</tag>
<tag>
<name>userGroupPicker</name>
<tag-class>org.alfresco.web.ui.repo.tag.UserGroupPickerTag</tag-class>
<body-content>JSP</body-content>
<description>
The userGroupPicker component renders a multi-select hierarchical list of groups
and users. The groups and be expanded to show the child users and groups for individual
selection and deselection.
</description>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>binding</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>actionListener</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>style</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>styleClass</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>

View File

@@ -519,3 +519,12 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl
padding: 4px;
-moz-border-radius: 4px;
}
.userGroupPickerList
{
padding: 2px;
background-color: #EEEEEE;
border-width: 1px;
border-style: solid;
border-color: #AAAAAA;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -185,7 +185,7 @@
<a:listItem value="details" label="#{msg.details_view}" />
<a:listItem value="icons" label="#{msg.view_icon}" />
<a:listItem value="list" label="#{msg.view_browse}" />
<a:listItem value="dashboard" label="#{msg.dashboard_view}" disabled="#{!NavigationBean.currentNodeHasTemplate}" />
<a:listItem value="dashboard" label="#{msg.custom_view}" disabled="#{!NavigationBean.currentNodeHasTemplate}" />
</a:modeList>
</td>
</tr>

View File

@@ -87,7 +87,7 @@
<a:listItem value="details" label="#{msg.details_view}" />
<a:listItem value="icons" label="#{msg.view_icon}" />
<a:listItem value="list" label="#{msg.view_browse}" />
<a:listItem value="dashboard" label="#{msg.dashboard_view}" />
<a:listItem value="dashboard" label="#{msg.custom_view}" />
</a:modeList>
</td>
</tr>
@@ -110,7 +110,7 @@
<td>
<div style="padding:4px">
<a:panel id="dashboard-panel" border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle" label="#{msg.dashboard}">
<a:panel id="dashboard-panel" border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE" styleClass="mainSubTitle" label="#{msg.custom_view}">
<r:template id="template" template="#{NavigationBean.currentNodeTemplate}" model="#{NavigationBean.templateModel}" />

View File

@@ -69,7 +69,7 @@
<h:graphicImage url="/images/icons/preview_large.gif"/>
</td>
<td>
<div class="mainTitle"><h:outputText value="#{msg.apply_dashboard}" /> '<h:outputText value="#{DocumentDetailsBean.name}" />'</div>
<div class="mainTitle"><h:outputText value="#{msg.apply_template}" /> '<h:outputText value="#{DocumentDetailsBean.name}" />'</div>
<div class="mainSubText"><h:outputText value="#{msg.apply_dashboard_doc_info}" /></div>
</td>
</tr>

View File

@@ -69,7 +69,7 @@
<h:graphicImage url="/images/icons/preview_large.gif"/>
</td>
<td>
<div class="mainTitle"><h:outputText value="#{msg.apply_dashboard}" /> '<h:outputText value="#{SpaceDetailsBean.name}" />'</div>
<div class="mainTitle"><h:outputText value="#{msg.apply_template}" /> '<h:outputText value="#{SpaceDetailsBean.name}" />'</div>
<div class="mainSubText"><h:outputText value="#{msg.apply_dashboard_info}" /></div>
</td>
</tr>

View File

@@ -125,14 +125,14 @@
</r:permissionEvaluator>
</f:facet>
</h:panelGroup>
<a:panel label="#{msg.dashboard_view}" id="dashboard-panel" progressive="true" facetsId="dashboard-panel-facets"
<a:panel label="#{msg.custom_view}" id="dashboard-panel" progressive="true" facetsId="dashboard-panel-facets"
border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE"
expanded='#{DocumentDetailsBean.panels["dashboard-panel"]}' expandedActionListener="#{DocumentDetailsBean.expandPanel}">
<table width=100% cellspacing=0 cellpadding=0 border=0>
<tr>
<td align=left>
<r:permissionEvaluator value="#{DocumentDetailsBean.document}" allow="Write" id="evalApply">
<a:actionLink id="actDashboard" value="#{msg.apply_dashboard}" rendered="#{DocumentDetailsBean.templatable == false}" action="dialog:applyTemplate" />
<a:actionLink id="actDashboard" value="#{msg.apply_template}" rendered="#{DocumentDetailsBean.templatable == false}" action="dialog:applyTemplate" />
</r:permissionEvaluator>
<a:panel id="template-panel" rendered="#{DocumentDetailsBean.templatable == true}">
<div style="padding:4px;border: 1px dashed #cccccc">

View File

@@ -113,14 +113,14 @@
</r:permissionEvaluator>
</f:facet>
</h:panelGroup>
<a:panel label="#{msg.dashboard_view}" id="dashboard-panel" progressive="true" facetsId="dashboard-panel-facets"
<a:panel label="#{msg.custom_view}" id="dashboard-panel" progressive="true" facetsId="dashboard-panel-facets"
border="white" bgcolor="white" titleBorder="blue" titleBgcolor="#D3E6FE"
expanded='#{SpaceDetailsBean.panels["dashboard-panel"]}' expandedActionListener="#{SpaceDetailsBean.expandPanel}">
<table width=100% cellspacing=0 cellpadding=0 border=0>
<tr>
<td align=left>
<r:permissionEvaluator value="#{SpaceDetailsBean.space}" allow="Write" id="evalApply">
<a:actionLink id="actDashboard" value="#{msg.apply_dashboard}" rendered="#{SpaceDetailsBean.templatable == false}" action="dialog:applyTemplate" />
<a:actionLink id="actDashboard" value="#{msg.apply_template}" rendered="#{SpaceDetailsBean.templatable == false}" action="dialog:applyTemplate" />
</r:permissionEvaluator>
<a:panel id="template-panel" rendered="#{SpaceDetailsBean.templatable == true}">
<div style="padding:4px;border: 1px dashed #cccccc">

View File

@@ -0,0 +1,74 @@
<%--
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.
--%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
<f:verbatim>
<script type="text/javascript">
window.onload = pageLoaded;
function pageLoaded()
{
document.getElementById("dialog:dialog-body:subject").focus();
checkButtonState();
}
function checkButtonState()
{
if (document.getElementById("dialog:dialog-body:subject").value.length == 0)
{
document.getElementById("dialog:finish-button").disabled = true;
}
else
{
document.getElementById("dialog:finish-button").disabled = false;
}
}
</script>
</f:verbatim>
<h:outputText styleClass="mainSubTitle" value="#{msg.message_recipients}" />
<f:verbatim><div style='padding:2px'></div></f:verbatim>
<r:userGroupPicker value="#{DialogManager.bean.usersGroups}" actionListener="#{DialogManager.bean.userGroupSelectorAction}" />
<f:verbatim><div style='padding:8px'></div></f:verbatim>
<h:outputText styleClass="mainSubTitle" value="#{msg.email_message}" />
<h:panelGrid columns="2" cellpadding="2" cellspacing="2" border="0" width="100%">
<h:outputText value="#{msg.subject}:" />
<h:panelGroup>
<h:inputText id="subject" value="#{DialogManager.bean.mailHelper.subject}" size="75" maxlength="1024" onkeyup="javascript:checkButtonState();" />
<f:verbatim>&nbsp;*</f:verbatim>
</h:panelGroup>
<f:verbatim></f:verbatim>
<h:panelGrid columns="4" cellspacing="1" cellpadding="1" border="0">
<h:outputText value="#{msg.action_mail_template}:" />
<h:selectOneMenu value="#{DialogManager.bean.mailHelper.template}">
<f:selectItems value="#{TemplateSupportBean.emailTemplates}" />
</h:selectOneMenu>
<h:commandButton value="#{msg.insert_template}" actionListener="#{DialogManager.bean.mailHelper.insertTemplate}" styleClass="wizardButton" />
<h:commandButton value="#{msg.discard_template}" actionListener="#{DialogManager.bean.mailHelper.discardTemplate}" styleClass="wizardButton" disabled="#{DialogManager.bean.mailHelper.usingTemplate == null}" />
</h:panelGrid>
<h:outputText value="#{msg.message}:"/>
<h:inputTextarea value="#{DialogManager.bean.mailHelper.body}" rows="4" cols="75" disabled="#{DialogManager.bean.mailHelper.usingTemplate != null}" />
</h:panelGrid>

View File

@@ -134,7 +134,7 @@
<tr>
<td style="padding-left:16px"><h:outputText value="#{msg.subject}"/>:</td>
<td>
<h:inputText id="subject" value="#{InviteContentUsersWizard.subject}" size="75" maxlength="1024" />&nbsp;*
<h:inputText id="subject" value="#{InviteContentUsersWizard.mailHelper.subject}" size="75" maxlength="1024" />&nbsp;*
</td>
</tr>
@@ -146,12 +146,12 @@
<td><h:outputText value="#{msg.action_mail_template}"/>:</td>
<td>
<%-- Templates drop-down selector --%>
<h:selectOneMenu value="#{InviteContentUsersWizard.template}">
<h:selectOneMenu value="#{InviteContentUsersWizard.mailHelper.template}">
<f:selectItems value="#{TemplateSupportBean.emailTemplates}" />
</h:selectOneMenu>
</td>
<td><h:commandButton value="#{msg.insert_template}" actionListener="#{InviteContentUsersWizard.insertTemplate}" styleClass="wizardButton" /></td>
<td><h:commandButton value="#{msg.discard_template}" actionListener="#{InviteContentUsersWizard.discardTemplate}" styleClass="wizardButton" disabled="#{InviteContentUsersWizard.usingTemplate == null}" /></td>
<td><h:commandButton value="#{msg.insert_template}" actionListener="#{InviteContentUsersWizard.mailHelper.insertTemplate}" styleClass="wizardButton" /></td>
<td><h:commandButton value="#{msg.discard_template}" actionListener="#{InviteContentUsersWizard.mailHelper.discardTemplate}" styleClass="wizardButton" disabled="#{InviteContentUsersWizard.mailHelper.usingTemplate == null}" /></td>
</tr>
</table>
</td>
@@ -160,8 +160,8 @@
<tr>
<td style="padding-left:16px"><h:outputText value="#{msg.message}"/>:</td>
<td>
<h:inputTextarea value="#{InviteContentUsersWizard.body}"
rows="4" cols="75" disabled="#{InviteContentUsersWizard.usingTemplate != null}" />
<h:inputTextarea value="#{InviteContentUsersWizard.mailHelper.body}"
rows="4" cols="75" disabled="#{InviteContentUsersWizard.mailHelper.usingTemplate != null}" />
</td>
</tr>

View File

@@ -134,7 +134,7 @@
<tr>
<td style="padding-left:16px"><h:outputText value="#{msg.subject}"/>:</td>
<td>
<h:inputText id="subject" value="#{InviteSpaceUsersWizard.subject}" size="75" maxlength="1024" />&nbsp;*
<h:inputText id="subject" value="#{InviteSpaceUsersWizard.mailHelper.subject}" size="75" maxlength="1024" />&nbsp;*
</td>
</tr>
@@ -146,12 +146,12 @@
<td><h:outputText value="#{msg.action_mail_template}"/>:</td>
<td>
<%-- Templates drop-down selector --%>
<h:selectOneMenu value="#{InviteSpaceUsersWizard.template}">
<h:selectOneMenu value="#{InviteSpaceUsersWizard.mailHelper.template}">
<f:selectItems value="#{TemplateSupportBean.emailTemplates}" />
</h:selectOneMenu>
</td>
<td><h:commandButton value="#{msg.insert_template}" actionListener="#{InviteSpaceUsersWizard.insertTemplate}" styleClass="wizardButton" /></td>
<td><h:commandButton value="#{msg.discard_template}" actionListener="#{InviteSpaceUsersWizard.discardTemplate}" styleClass="wizardButton" disabled="#{InviteSpaceUsersWizard.usingTemplate == null}" /></td>
<td><h:commandButton value="#{msg.insert_template}" actionListener="#{InviteSpaceUsersWizard.mailHelper.insertTemplate}" styleClass="wizardButton" /></td>
<td><h:commandButton value="#{msg.discard_template}" actionListener="#{InviteSpaceUsersWizard.mailHelper.discardTemplate}" styleClass="wizardButton" disabled="#{InviteSpaceUsersWizard.mailHelper.usingTemplate == null}" /></td>
</tr>
</table>
</td>
@@ -160,8 +160,8 @@
<tr>
<td style="padding-left:16px"><h:outputText value="#{msg.message}"/>:</td>
<td>
<h:inputTextarea value="#{InviteSpaceUsersWizard.body}"
rows="4" cols="75" disabled="#{InviteSpaceUsersWizard.usingTemplate != null}" />
<h:inputTextarea value="#{InviteSpaceUsersWizard.mailHelper.body}"
rows="4" cols="75" disabled="#{InviteSpaceUsersWizard.mailHelper.usingTemplate != null}" />
</td>
</tr>