[ACS-777] Email notification for digital workspace

This commit is contained in:
dhrn
2020-11-03 15:03:43 +05:30
parent 8bf87153bf
commit 2933d45eff
17 changed files with 778 additions and 393 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -45,15 +45,25 @@ public interface SiteMembershipRequests
/** /**
* Create a site membership request for the user 'inviteeId' * Create a site membership request for the user 'inviteeId'
* @param inviteeId the site inviteee id * @param inviteeId the site invite id
* @param siteInvite the site invite * @param siteInvite the site invite
* @return SiteMembershipRequest * @return SiteMembershipRequest
*/ */
SiteMembershipRequest createSiteMembershipRequest(String inviteeId, final SiteMembershipRequest siteInvite); SiteMembershipRequest createSiteMembershipRequest(String inviteeId, final SiteMembershipRequest siteInvite);
/**
* Create a site membership request for the user 'inviteeId'
* @param inviteeId the site invitee id
* @param siteInvite the site invite
* @param client the client name which is registered to send emails
* @param workspacePath path of workspace deployed location
* @return SiteMembershipRequest
*/
SiteMembershipRequest createSiteMembershipRequest(String inviteeId, final SiteMembershipRequest siteInvite, final String client, final String workspacePath);
/** /**
* Update the site membership request for inviteeId and site * Update the site membership request for inviteeId and site
* @param inviteeId the site inviteee id * @param inviteeId the site invite id
* @param siteInvite the site invite * @param siteInvite the site invite
* @return the updated siteMembershipRequest * @return the updated siteMembershipRequest
*/ */
@@ -62,7 +72,7 @@ public interface SiteMembershipRequests
/** /**
* Cancel site membership request for invitee and site. * Cancel site membership request for invitee and site.
* *
* @param inviteeId the site inviteee id * @param inviteeId the site invite id
* @param siteId the site id * @param siteId the site id
*/ */
void cancelSiteMembershipRequest(String inviteeId, String siteId); void cancelSiteMembershipRequest(String inviteeId, String siteId);
@@ -70,7 +80,7 @@ public interface SiteMembershipRequests
/** /**
* Get the site membership request for inviteeId and siteId, if it exists. * Get the site membership request for inviteeId and siteId, if it exists.
* *
* @param inviteeId the site inviteee id * @param inviteeId the site invite id
* @param siteId the site id * @param siteId the site id
* @return the site membership request * @return the site membership request
*/ */
@@ -79,7 +89,7 @@ public interface SiteMembershipRequests
/** /**
* Get a paged list of site membership requests for inviteeId. * Get a paged list of site membership requests for inviteeId.
* *
* @param inviteeId the site inviteee id * @param inviteeId the site invite id
* @param paging paging information * @param paging paging information
* @return a paged list of site membership requests * @return a paged list of site membership requests
*/ */

View File

@@ -158,9 +158,9 @@ public class SiteMembershipRequestsImpl implements SiteMembershipRequests
} }
private SiteMembershipRequest inviteToModeratedSite(final String message, final String inviteeId, final String siteId, private SiteMembershipRequest inviteToModeratedSite(final String message, final String inviteeId, final String siteId,
final String inviteeRole) final String inviteeRole, final String clientName, final String workspacePath)
{ {
ModeratedInvitation invitation = invitationService.inviteModerated(message, inviteeId, ResourceType.WEB_SITE, siteId, inviteeRole); ModeratedInvitation invitation = invitationService.inviteModerated(message, inviteeId, ResourceType.WEB_SITE, siteId, inviteeRole, clientName, workspacePath);
SiteMembershipRequest ret = new SiteMembershipRequest(); SiteMembershipRequest ret = new SiteMembershipRequest();
ret.setId(siteId); ret.setId(siteId);
@@ -270,7 +270,74 @@ public class SiteMembershipRequestsImpl implements SiteMembershipRequests
if(siteVisibility.equals(SiteVisibility.MODERATED)) if(siteVisibility.equals(SiteVisibility.MODERATED))
{ {
request = inviteToModeratedSite(message, inviteeId, siteId, inviteeRole); request = inviteToModeratedSite(message, inviteeId, siteId, inviteeRole, null, null);
}
else if(siteVisibility.equals(SiteVisibility.PUBLIC))
{
request = inviteToPublicSite(siteInfo, message, inviteeId, inviteeRole);
}
else
{
// note: security, no indication that this is a private site
throw new RelationshipResourceNotFoundException(inviteeId, siteId);
}
return request;
}
@Override
public SiteMembershipRequest createSiteMembershipRequest(String inviteeId, SiteMembershipRequest siteInvite, String client, String workspacePath) {
SiteMembershipRequest request = null;
inviteeId = people.validatePerson(inviteeId, true);
// Note that the order of error checking is important. The server first needs to check for the status 404
// conditions before checking for status 400 conditions. Otherwise the server is open to a probing attack.
String siteId = siteInvite.getId();
final SiteInfo siteInfo = sites.validateSite(siteId);
if(siteInfo == null)
{
// site does not exist
throw new RelationshipResourceNotFoundException(inviteeId, siteId);
}
// set the site id to the short name (to deal with case sensitivity issues with using the siteId from the url)
siteId = siteInfo.getShortName();
final SiteVisibility siteVisibility = siteInfo.getVisibility();
if(siteVisibility.equals(SiteVisibility.PRIVATE))
{
// note: security, no indication that this is a private site
throw new RelationshipResourceNotFoundException(inviteeId, siteId);
}
// Is the invitee already a member of the site?
boolean isMember = siteService.isMember(siteId, inviteeId);
if(isMember)
{
// yes
throw new InvalidArgumentException(inviteeId + " is already a member of site " + siteId);
}
// Is there an outstanding site invite request for the (invitee, site)?
Invitation invitation = getSiteInvitation(inviteeId, siteId);
if(invitation != null)
{
// yes
throw new InvalidArgumentException(inviteeId + " is already invited to site " + siteId);
}
final String inviteeRole = DEFAULT_ROLE;
String message = siteInvite.getMessage();
if(message == null)
{
// the invitation service ignores null messages so convert to an empty message.
message = "";
}
if(siteVisibility.equals(SiteVisibility.MODERATED))
{
request = inviteToModeratedSite(message, inviteeId, siteId, inviteeRole, client, workspacePath);;
} }
else if(siteVisibility.equals(SiteVisibility.PUBLIC)) else if(siteVisibility.equals(SiteVisibility.PUBLIC))
{ {

View File

@@ -73,7 +73,14 @@ RelationshipResourceAction.Create<SiteMembershipRequest>, RelationshipResourceAc
List<SiteMembershipRequest> result = new ArrayList<SiteMembershipRequest>(invites.size()); List<SiteMembershipRequest> result = new ArrayList<SiteMembershipRequest>(invites.size());
for(SiteMembershipRequest invite : invites) for(SiteMembershipRequest invite : invites)
{ {
SiteMembershipRequest siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite); SiteMembershipRequest siteInvite = null;
String client = parameters.getParameter("client");
String workspacePath = parameters.getParameter("workspacePath");
if(client != null) {
siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite, client, workspacePath);
} else {
siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite);
}
result.add(siteInvite); result.add(siteInvite);
} }
return result; return result;

View File

@@ -42,6 +42,7 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
public static final String ROLE_KEY = "role"; public static final String ROLE_KEY = "role";
public static final String CREATED_AT = "createdAt"; public static final String CREATED_AT = "createdAt";
public static final String MODIFIED_AT = "modifiedAt"; public static final String MODIFIED_AT = "modifiedAt";
public static final String CLIENT_NAME = "clientName";
/** /**
* Unique reference for this invitation * Unique reference for this invitation
@@ -72,6 +73,8 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
private final Date modifiedAt; private final Date modifiedAt;
private final Date clientName;
public InvitationImpl(Map<String, Serializable> props) public InvitationImpl(Map<String, Serializable> props)
{ {
this.inviteId = (String)props.get(ID_KEY); this.inviteId = (String)props.get(ID_KEY);
@@ -82,6 +85,7 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
this.resourceType = type==null ? ResourceType.WEB_SITE : ResourceType.valueOf(type); this.resourceType = type==null ? ResourceType.WEB_SITE : ResourceType.valueOf(type);
this.createdAt = (Date)props.get(CREATED_AT); this.createdAt = (Date)props.get(CREATED_AT);
this.modifiedAt = (Date)props.get(MODIFIED_AT); this.modifiedAt = (Date)props.get(MODIFIED_AT);
this.clientName = (Date)props.get(CLIENT_NAME);
} }
/** /**
@@ -123,5 +127,10 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
return inviteeUserName; return inviteeUserName;
} }
public Date getClientName()
{
return clientName;
}
public abstract InvitationType getInvitationType(); public abstract InvitationType getInvitationType();
} }

View File

@@ -51,6 +51,7 @@ import java.util.Set;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.client.config.ClientAppConfig;
import org.alfresco.repo.i18n.MessageService; import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate; import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate;
import org.alfresco.repo.invitation.site.InviteModeratedSender; import org.alfresco.repo.invitation.site.InviteModeratedSender;
@@ -145,7 +146,9 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
WorkflowModelModeratedInvitation.wfVarInviteeRole, WorkflowModelModeratedInvitation.wfVarInviteeRole,
WorkflowModelModeratedInvitation.wfVarResourceName, WorkflowModelModeratedInvitation.wfVarResourceName,
WorkflowModelModeratedInvitation.bpmGroupAssignee, WorkflowModelModeratedInvitation.bpmGroupAssignee,
WorkflowModelModeratedInvitation.wfVarResourceType); WorkflowModelModeratedInvitation.wfVarResourceType,
WorkflowModelModeratedInvitation.wfVarTemplateAssetsUrl,
WorkflowModelModeratedInvitation.wfVarWorkspaceUrl);
/** /**
@@ -172,6 +175,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private MessageService messageService; private MessageService messageService;
private InviteNominatedSender inviteNominatedSender; private InviteNominatedSender inviteNominatedSender;
private InviteModeratedSender inviteModeratedSender; private InviteModeratedSender inviteModeratedSender;
private ClientAppConfig clientAppConfig;
// maximum number of tries to generate a invitee user name which // maximum number of tries to generate a invitee user name which
// does not already belong to an existing person // does not already belong to an existing person
@@ -194,6 +198,11 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private String moderatedInvitationWorkflowId = private String moderatedInvitationWorkflowId =
WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME_ACTIVITI; WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME_ACTIVITI;
public void setClientAppConfig(ClientAppConfig clientAppConfig)
{
this.clientAppConfig = clientAppConfig;
}
/** /**
* Set the policy component * Set the policy component
* *
@@ -2042,4 +2051,71 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
{ {
return CollectionUtils.filterKeys((Map<String, String>) executionVariables, CollectionUtils.containsFilter(propertyNames)); return CollectionUtils.filterKeys((Map<String, String>) executionVariables, CollectionUtils.containsFilter(propertyNames));
} }
/**
* Start the invitation process for a ModeratedInvitation
*
* @param inviteeComments why does the invitee want access to the resource ?
* @param inviteeUserName who is to be invited
* @param resourceType Invitation .ResourceType what resource type ?
* @param resourceName which resource
* @param inviteeRole which role ?
* @param clientName which client
*/
public ModeratedInvitation inviteModerated(String inviteeComments, String inviteeUserName,
Invitation.ResourceType resourceType, String resourceName, String inviteeRole, String clientName, String workspacePath)
{
if (resourceType == Invitation.ResourceType.WEB_SITE)
{
return startModeratedInvite(inviteeComments, inviteeUserName, resourceType, resourceName, inviteeRole, clientName, workspacePath);
}
throw new InvitationException("unknown resource type");
}
/**
* Moderated invitation implementation for given client
*
* @return the new moderated invitation
*/
private ModeratedInvitation startModeratedInvite(String inviteeComments, String inviteeUserName,
Invitation.ResourceType resourceType, String resourceName, String inviteeRole, String clientName, String workspacePath)
{
SiteInfo siteInfo = siteService.getSite(resourceName);
if (siteService.isMember(resourceName, inviteeUserName))
{
if (logger.isDebugEnabled())
logger.debug("Failed - invitee user is already a member of the site.");
Object objs[] = { inviteeUserName, "", resourceName };
throw new InvitationExceptionUserError("invitation.invite.already_member", objs);
}
String roleGroup = siteService.getSiteRoleGroup(resourceName, SiteModel.SITE_MANAGER);
// get the workflow description
String workflowDescription = generateWorkflowDescription(siteInfo, "invitation.moderated.workflow.description");
Map<QName, Serializable> workflowProps = new HashMap<QName, Serializable>(16);
workflowProps.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, workflowDescription);
workflowProps.put(WorkflowModelModeratedInvitation.ASSOC_GROUP_ASSIGNEE, roleGroup);
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_COMMENTS, inviteeComments);
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_ROLE, inviteeRole);
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_USER_NAME, inviteeUserName);
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_NAME, resourceName);
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE, resourceType.toString());
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_CLIENT_NAME, clientName);
if(clientName != null && clientAppConfig.getClient(clientName) != null) {
ClientAppConfig.ClientApp client = clientAppConfig.getClient(clientName);
workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getTemplateAssetsUrl());
String workspaceUrl = workspacePath != null ? workspacePath : client.getProperty("workspaceUrl");
workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, workspaceUrl);
}
// get the moderated workflow
WorkflowDefinition wfDefinition = getWorkflowDefinition(InvitationWorkflowType.MODERATED);
return (ModeratedInvitation) startWorkflow(wfDefinition, workflowProps);
}
} }

View File

@@ -31,6 +31,7 @@ import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_P
import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_MODIFIED_AT; import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_MODIFIED_AT;
import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_NAME; import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_NAME;
import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE; import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE;
import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.WF_PROP_CLIENT_NAME;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@@ -77,6 +78,7 @@ import org.alfresco.service.namespace.QName;
parentProps.put(RESOURCE_TYPE_KEY,(String)props.get(WF_PROP_RESOURCE_TYPE)); parentProps.put(RESOURCE_TYPE_KEY,(String)props.get(WF_PROP_RESOURCE_TYPE));
parentProps.put(CREATED_AT,(Date)props.get(ContentModel.PROP_CREATED)); parentProps.put(CREATED_AT,(Date)props.get(ContentModel.PROP_CREATED));
parentProps.put(MODIFIED_AT,(Date)props.get(WF_PROP_MODIFIED_AT)); parentProps.put(MODIFIED_AT,(Date)props.get(WF_PROP_MODIFIED_AT));
parentProps.put(CLIENT_NAME,(Date)props.get(WF_PROP_CLIENT_NAME));
return parentProps; return parentProps;
} }

View File

@@ -66,6 +66,11 @@ public interface WorkflowModelModeratedInvitation
public static final QName WF_PROP_REVIEWER= QName.createQName(NAMESPACE_URI, "reviewer"); public static final QName WF_PROP_REVIEWER= QName.createQName(NAMESPACE_URI, "reviewer");
public static final QName WF_PROP_MODIFIED_AT= QName.createQName(NAMESPACE_URI, "modifiedAt"); public static final QName WF_PROP_MODIFIED_AT= QName.createQName(NAMESPACE_URI, "modifiedAt");
public static final QName WF_PROP_CLIENT_NAME= QName.createQName(NAMESPACE_URI, "clientName");
public static final QName WF_WORKSPACE_URL = QName.createQName(NAMESPACE_URI, "workspaceUrl");
public static final QName WF_SHARED_LINK_BASE_URL = QName.createQName(NAMESPACE_URI, "sharedLinkBaseUrl");
public static final QName WF_TEMPLATE_ASSETS_URL = QName.createQName(NAMESPACE_URI, "templateAssetsUrl");
// workflow execution context variable names // workflow execution context variable names
public static final String wfVarInviteeUserName = "imwf_inviteeUserName"; public static final String wfVarInviteeUserName = "imwf_inviteeUserName";
public static final String wfVarInviteeRole = "imwf_inviteeRole"; public static final String wfVarInviteeRole = "imwf_inviteeRole";
@@ -75,4 +80,8 @@ public interface WorkflowModelModeratedInvitation
public static final String wfVarReviewer = "imwf_reviewer"; public static final String wfVarReviewer = "imwf_reviewer";
public static final String wfVarReviewComments = "imwf_reviewComments"; public static final String wfVarReviewComments = "imwf_reviewComments";
public static final String bpmGroupAssignee = "bpm_groupAssignee"; public static final String bpmGroupAssignee = "bpm_groupAssignee";
public static final String wfVarClientName = "imwf_clientName";
public static final String wfVarWorkspaceUrl = "imwf_workspaceUrl";
public static final String wfVarTemplateAssetsUrl = "imwf_templateAssetsUrl";
} }

View File

@@ -28,7 +28,10 @@ package org.alfresco.repo.invitation.activiti;
import java.util.Map; import java.util.Map;
import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.DelegateExecution;
import org.alfresco.repo.client.config.ClientAppConfig;
import org.alfresco.repo.invitation.WorkflowModelModeratedInvitation;
import org.alfresco.repo.workflow.activiti.ActivitiConstants; import org.alfresco.repo.workflow.activiti.ActivitiConstants;
import org.alfresco.util.EmailHelper;
/** /**
* Activiti delegate that is executed when a invitation request has been sent. * Activiti delegate that is executed when a invitation request has been sent.
@@ -39,10 +42,12 @@ public class SendModeratedInviteDelegate extends AbstractInvitationDelegate
{ {
private String emailTemplatePath; private String emailTemplatePath;
private ClientAppConfig clientAppConfig;
private EmailHelper emailHelper;
public static final String ENTERPRISE_EMAIL_TEMPLATE_PATH = "app:company_home/app:dictionary/app:email_templates/cm:invite/cm:invite-email-moderated.html.ftl"; public static final String ENTERPRISE_EMAIL_TEMPLATE_PATH = "app:company_home/app:dictionary/app:email_templates/cm:invite/cm:invite-email-moderated.html.ftl";
public static final String EMAIL_SUBJECT_KEY = public static final String EMAIL_SUBJECT_KEY = "invitation.moderated.email.subject";
"invitation.moderated.email.subject"; private static final String EMAIL_TEMPLATE_REF ="alfresco/templates/workspace/invite-email-moderated.html.ftl";
public void setEmailTemplatePath(String emailTemplatePath) public void setEmailTemplatePath(String emailTemplatePath)
@@ -50,11 +55,33 @@ public class SendModeratedInviteDelegate extends AbstractInvitationDelegate
this.emailTemplatePath = emailTemplatePath; this.emailTemplatePath = emailTemplatePath;
} }
public void setClientAppConfig(ClientAppConfig clientAppConfig)
{
this.clientAppConfig = clientAppConfig;
}
public void setEmailHelper(EmailHelper emailHelper)
{
this.emailHelper = emailHelper;
}
@Override @Override
public void execute(DelegateExecution execution) throws Exception public void execute(DelegateExecution execution) throws Exception
{ {
String invitationId = ActivitiConstants.ENGINE_ID + "$" + execution.getProcessInstanceId(); String invitationId = ActivitiConstants.ENGINE_ID + "$" + execution.getProcessInstanceId();
Map<String, Object> variables = execution.getVariables(); Map<String, Object> variables = execution.getVariables();
String clientName = (String) variables.get(WorkflowModelModeratedInvitation.wfVarClientName);
ClientAppConfig.ClientApp clientApp = clientAppConfig.getClient(clientName);
if(clientApp != null)
{
final String path = clientApp.getProperty("inviteModeratedTemplatePath");
final String templatePath = emailHelper.getEmailTemplate(clientApp.getName(), path, EMAIL_TEMPLATE_REF);
invitationService.sendModeratedInvitation(invitationId, templatePath, EMAIL_SUBJECT_KEY, variables);
}
else
{
invitationService.sendModeratedInvitation(invitationId, emailTemplatePath, EMAIL_SUBJECT_KEY, variables); invitationService.sendModeratedInvitation(invitationId, emailTemplatePath, EMAIL_SUBJECT_KEY, variables);
} }
} }
}

View File

@@ -47,6 +47,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.security.PersonService.PersonInfo; import org.alfresco.service.cmr.security.PersonService.PersonInfo;
import org.alfresco.util.ModelUtil; import org.alfresco.util.ModelUtil;
import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -60,6 +61,7 @@ public class InviteModeratedSender extends InviteSender
private static final String DATA_DICTIONARY_XPATH_PREFIX = "app:"; private static final String DATA_DICTIONARY_XPATH_PREFIX = "app:";
public static final String WF_PACKAGE = "wf_package"; public static final String WF_PACKAGE = "wf_package";
public static final String SHARE_PENDING_INVITES_LINK = "{0}/page/site/{1}/pending-invites"; public static final String SHARE_PENDING_INVITES_LINK = "{0}/page/site/{1}/pending-invites";
public static final String WORKSPACE_PENDING_INVITES_LINK = "{0}/#/{1}/members/libraries";
private static final List<String> INVITE_MODERATED_EXPECTED_PROPERTIES = Arrays.asList( private static final List<String> INVITE_MODERATED_EXPECTED_PROPERTIES = Arrays.asList(
WorkflowModelModeratedInvitation.wfVarInviteeUserName, WorkflowModelModeratedInvitation.wfVarInviteeUserName,
@@ -107,6 +109,8 @@ public class InviteModeratedSender extends InviteSender
model.put("inviteeName", StringUtils.join(new String[] { inviteePerson.getFirstName(), inviteePerson.getLastName() }, " ")); model.put("inviteeName", StringUtils.join(new String[] { inviteePerson.getFirstName(), inviteePerson.getLastName() }, " "));
model.put("siteName", getSiteName(properties)); model.put("siteName", getSiteName(properties));
model.put("sharePendingInvitesLink", StringUtils.stripStart(getPendingInvitesLink(properties), "/")); model.put("sharePendingInvitesLink", StringUtils.stripStart(getPendingInvitesLink(properties), "/"));
model.put("workspacePendingInvitesLink", StringUtils.stripStart(getWorkSpaceInvitesLink(properties), "/"));
model.put("template_assets_url", getTemplateAssetsLink(properties));
return model; return model;
} }
@@ -116,6 +120,24 @@ public class InviteModeratedSender extends InviteSender
properties.get(WorkflowModelModeratedInvitation.wfVarResourceName)); properties.get(WorkflowModelModeratedInvitation.wfVarResourceName));
} }
protected String getWorkSpaceInvitesLink(Map<String, String> properties)
{
String path = properties.get(WorkflowModelModeratedInvitation.wfVarWorkspaceUrl);
boolean hasValidBaseUrl = path != null && !StringUtils.isAllBlank(path) && path.length() > 1;
String workspaceUrl = sysAdminParams.getAlfrescoProtocol() + "://" + sysAdminParams.getAlfrescoHost() + ":" + sysAdminParams.getAlfrescoPort()
+ ( hasValidBaseUrl ? "/" + path.trim() : "");
return MessageFormat.format(WORKSPACE_PENDING_INVITES_LINK, workspaceUrl, properties.get(WorkflowModelModeratedInvitation.wfVarResourceName));
}
protected String getTemplateAssetsLink(Map<String, String> properties)
{
if (properties.get(WorkflowModelModeratedInvitation.wfVarTemplateAssetsUrl) != null) {
return UrlUtil.replaceAlfrescoUrlPlaceholder(properties.get(WorkflowModelModeratedInvitation.wfVarTemplateAssetsUrl), this.sysAdminParams);
}
return UrlUtil.getAlfrescoUrl(this.sysAdminParams);
}
@Override @Override
public List<String> getRequiredProperties() public List<String> getRequiredProperties()
{ {

View File

@@ -34,6 +34,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.i18n.MessageService; import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.model.Repository; import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
@@ -66,6 +67,7 @@ public abstract class InviteSender
protected final FileFolderService fileFolderService; protected final FileFolderService fileFolderService;
protected final RepoAdminService repoAdminService; protected final RepoAdminService repoAdminService;
protected final NamespaceService namespaceService; protected final NamespaceService namespaceService;
protected final SysAdminParams sysAdminParams;
public InviteSender(ServiceRegistry services, Repository repository, MessageService messageService) public InviteSender(ServiceRegistry services, Repository repository, MessageService messageService)
{ {
@@ -79,6 +81,7 @@ public abstract class InviteSender
this.namespaceService = services.getNamespaceService(); this.namespaceService = services.getNamespaceService();
this.repository = repository; this.repository = repository;
this.messageService = messageService; this.messageService = messageService;
this.sysAdminParams = services.getSysAdminParams();
} }
/** /**

View File

@@ -138,6 +138,19 @@ public interface InvitationService
@Auditable(parameters = { "inviteeComments", "inviteeUserName", "resourceType", "resourceName", "inviteeRole" }) @Auditable(parameters = { "inviteeComments", "inviteeUserName", "resourceType", "resourceName", "inviteeRole" })
public ModeratedInvitation inviteModerated(String inviteeComments, String inviteeUserName, Invitation.ResourceType resourceType, String resourceName, String inviteeRole); public ModeratedInvitation inviteModerated(String inviteeComments, String inviteeUserName, Invitation.ResourceType resourceType, String resourceName, String inviteeRole);
/**
* Start the invitation process for a ModeratedInvitation
*
* @param inviteeUserName who is to be invited
* @param Invitation.ResourceType resourceType what resource type ?
* @param resourceName which resource
* @param inviteeRole which role ?
* @param clientName which client
* @param workspacePath path of the digital workspace
*/
@Auditable(parameters = { "inviteeComments", "inviteeUserName", "resourceType", "resourceName", "inviteeRole", "clientName", "workspacePath" })
public ModeratedInvitation inviteModerated(String inviteeComments, String inviteeUserName, Invitation.ResourceType resourceType, String resourceName, String inviteeRole, String clientName, String workspacePath);
/** /**
* Update the invitee comments for an existing moderated invitation * Update the invitee comments for an existing moderated invitation
* *

View File

@@ -39,7 +39,8 @@ public class UrlUtil
{ {
// ${shareUrl} placeholder // ${shareUrl} placeholder
public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}"); public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}");
// ${alfrescoUrl} placeholder
public static final Pattern REPO_PATTERN = Pattern.compile("\\$\\{alfrescoUrl\\}");
/** /**
* Builds up the Url to Alfresco based on the settings in the * Builds up the Url to Alfresco based on the settings in the
* {@link SysAdminParams}. * {@link SysAdminParams}.
@@ -113,6 +114,15 @@ public class UrlUtil
return value; return value;
} }
public static String replaceAlfrescoUrlPlaceholder(String value, SysAdminParams sysAdminParams)
{
if (value != null)
{
return REPO_PATTERN.matcher(value).replaceAll(getAlfrescoUrl(sysAdminParams));
}
return value;
}
protected static String buildUrl(String protocol, String host, int port, String context) protected static String buildUrl(String protocol, String host, int port, String context)
{ {
StringBuilder url = new StringBuilder(); StringBuilder url = new StringBuilder();

View File

@@ -119,5 +119,7 @@
<property name="emailTemplatePath"> <property name="emailTemplatePath">
<util:constant static-field="org.alfresco.repo.invitation.activiti.SendModeratedInviteDelegate.ENTERPRISE_EMAIL_TEMPLATE_PATH"/> <util:constant static-field="org.alfresco.repo.invitation.activiti.SendModeratedInviteDelegate.ENTERPRISE_EMAIL_TEMPLATE_PATH"/>
</property> </property>
<property name="clientAppConfig" ref="clientAppConfig"/>
<property name="emailHelper" ref="emailHelper"/>
</bean> </bean>
</beans> </beans>

View File

@@ -97,3 +97,7 @@ templates.generic-email.ftl.salutation=Hi {0},
templates.generic-email.ftl.copy_right=All Rights Reserved. templates.generic-email.ftl.copy_right=All Rights Reserved.
templates.generic-email.ftl.contact_us=Contact Us templates.generic-email.ftl.contact_us=Contact Us
#workspace invite-email-moderated.ftl
templates.workspace.invite-email-moderated.html.call-to-action=Join Request
templates.workspace.invite-email-moderated.html.body=has requested to join
templates.workspace.invite-email-moderated.html.action=Review request

View File

@@ -0,0 +1,121 @@
<html>
<head>
<style type="text/css">
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% ! important;
height: 100% !important;
color: #727174;
font-weight: 400;
font-size: 18px;
margin: 0;
}
</style>
</head>
<body>
<div bgcolor="transparent">
<center>
<table align="center" bgcolor="transparent" border="0" cellpadding="0" cellspacing="0" height="100%"
width="100%">
<tbody>
<tr>
<td align="center" valign="top" style="padding-bottom:60px">
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 25px;">
<tbody>
<tr>
<td align="center" valign="top">
<table align="center" bgcolor="#FFFFFF" border="0" cellpadding="0"
cellspacing="0"
style="background-color:#ffffff;max-width: 600px;max-height: 600px;"
width="100%">
<tbody>
<tr>
<td align="" valign="top" bgcolor="#FFFFFF"
style="padding: 40px;">
<img style="border:none; display:block; border-collapse:collapse; outline:none; text-decoration:none;"
src="${template_assets_url}/logo/workspace.png" border="0"
alt="Alfresco" width="180" height="36">
</td>
</tr>
<tr>
<td style="padding-right:40px;padding-bottom:30px;padding-left:40px">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<h3 style="letter-spacing: 0; color: #707070;font-size: 20px; margin: 0">
Join Request
</h3>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="padding-right:40px;padding-bottom:10px;padding-left:40px">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<p style="letter-spacing: 0px; color: #707070; display: block; font-weight: bold; font-size: 1.17em; margin: 0; ">
${inviteeName}
</p>
<p style="letter-spacing: 0px; color: #707070;">
${message("templates.workspace.invite-email-moderated.html.body")}
</p>
<p style="letter-spacing: 0px; color: #707070; display: block; font-weight: bold; font-size: 1.17em; ">
${siteName}
</p>
<p style="letter-spacing: 0px; color: #707070; display: block;">
<table bgcolor="#FFFFFF" border="0"
cellpadding="0" cellspacing="0"
style="background: #2A7DE1 0 0 no-repeat padding-box;border-radius: 5px;color: #FFFFFF;">
<tbody>
<tr>
<td>
<a href="${workspacePendingInvitesLink}"
style="color:#ffffff;display:inline-block;border-radius: 5px;font-size:16px;font-family:Helvetica,Arial,Verdana,sans-serif;font-weight:400;padding: 10px;text-decoration:none;"
target="_blank"
data-saferedirecturl="${workspacePendingInvitesLink}">
${message("templates.workspace.invite-email-moderated.html.action")}
</a>
</td>
</tr>
</tbody>
</table>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td align="center" valign="top" style="border-top:2px solid #efeeea;color:#6a655f;font-family:Helvetica,Arial,Verdana,sans-serif;font-size:12px;font-weight:400;line-height:24px;padding-top:40px;padding-bottom:40px;text-align:center">
<p style="color:#6a655f;font-family:Helvetica,Arial,Verdana,sans-serif;font-size:12px;font-weight:400;line-height:24px;padding:0 20px;margin:0;text-align:center">
© 2020 Alfresco Software, Inc. All rights reserved.
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</center>
</div>
</body>
</html>

View File

@@ -82,6 +82,9 @@
<property name="imwf:modifiedAt"> <property name="imwf:modifiedAt">
<type>d:date</type> <type>d:date</type>
</property> </property>
<property name="imwf:clientName">
<type>d:text</type>
</property>
</properties> </properties>
</aspect> </aspect>
</aspects> </aspects>