Merge remote-tracking branch 'origin/master'

This commit is contained in:
Alan Davis
2020-11-16 15:00:58 +00:00
24 changed files with 959 additions and 426 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -45,15 +45,24 @@ public interface SiteMembershipRequests
/**
* 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
* @return SiteMembershipRequest
*/
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
* @return SiteMembershipRequest
*/
SiteMembershipRequest createSiteMembershipRequest(String inviteeId, final SiteMembershipRequest siteInvite, final String client);
/**
* 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
* @return the updated siteMembershipRequest
*/
@@ -62,7 +71,7 @@ public interface SiteMembershipRequests
/**
* 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
*/
void cancelSiteMembershipRequest(String inviteeId, String siteId);
@@ -70,7 +79,7 @@ public interface SiteMembershipRequests
/**
* 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
* @return the site membership request
*/
@@ -79,7 +88,7 @@ public interface SiteMembershipRequests
/**
* 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
* @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,
final String inviteeRole)
final String inviteeRole, final String clientName)
{
ModeratedInvitation invitation = invitationService.inviteModerated(message, inviteeId, ResourceType.WEB_SITE, siteId, inviteeRole);
ModeratedInvitation invitation = invitationService.inviteModerated(message, inviteeId, ResourceType.WEB_SITE, siteId, inviteeRole, clientName);
SiteMembershipRequest ret = new SiteMembershipRequest();
ret.setId(siteId);
@@ -270,7 +270,76 @@ public class SiteMembershipRequestsImpl implements SiteMembershipRequests
if(siteVisibility.equals(SiteVisibility.MODERATED))
{
request = inviteToModeratedSite(message, inviteeId, siteId, inviteeRole);
request = inviteToModeratedSite(message, inviteeId, siteId, inviteeRole, 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)
{
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);
}
else if(siteVisibility.equals(SiteVisibility.PUBLIC))
{

View File

@@ -53,6 +53,7 @@ public class SiteMembershipRequest implements Comparable<SiteMembershipRequest>
private Date modifiedAt;
private String title; // for sorting only
private Person person;
private String client;
public static Pair<String, String> splitId(String id)
{
@@ -137,11 +138,21 @@ public class SiteMembershipRequest implements Comparable<SiteMembershipRequest>
this.person = person;
}
public String getClient()
{
return client;
}
public void setClient(String client)
{
this.client = client;
}
@Override
public String toString()
{
return "SiteMembershipRequest [id=" + id + ", message=" + message + ", createdAt=" + createdAt
+ ", modifiedAt=" + modifiedAt + "]";
+ ", modifiedAt=" + modifiedAt + ", client=" + client + "]";
}
@Override

View File

@@ -73,7 +73,14 @@ RelationshipResourceAction.Create<SiteMembershipRequest>, RelationshipResourceAc
List<SiteMembershipRequest> result = new ArrayList<SiteMembershipRequest>(invites.size());
for(SiteMembershipRequest invite : invites)
{
SiteMembershipRequest siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite);
SiteMembershipRequest siteInvite = null;
if(invite.getClient() != null)
{
siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite, invite.getClient());
} else
{
siteInvite = siteMembershipRequests.createSiteMembershipRequest(personId, invite);
}
result.add(siteInvite);
}
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 CREATED_AT = "createdAt";
public static final String MODIFIED_AT = "modifiedAt";
public static final String CLIENT_NAME = "clientName";
/**
* Unique reference for this invitation
@@ -72,6 +73,8 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
private final Date modifiedAt;
private final String clientName;
public InvitationImpl(Map<String, Serializable> props)
{
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.createdAt = (Date)props.get(CREATED_AT);
this.modifiedAt = (Date)props.get(MODIFIED_AT);
this.clientName = (String)props.get(CLIENT_NAME);
}
/**
@@ -123,5 +127,10 @@ import org.alfresco.service.cmr.invitation.Invitation.ResourceType;
return inviteeUserName;
}
public String getClientName()
{
return clientName;
}
public abstract InvitationType getInvitationType();
}

View File

@@ -51,6 +51,7 @@ import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.client.config.ClientAppConfig;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate;
import org.alfresco.repo.invitation.site.InviteModeratedSender;
@@ -145,7 +146,9 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
WorkflowModelModeratedInvitation.wfVarInviteeRole,
WorkflowModelModeratedInvitation.wfVarResourceName,
WorkflowModelModeratedInvitation.bpmGroupAssignee,
WorkflowModelModeratedInvitation.wfVarResourceType);
WorkflowModelModeratedInvitation.wfVarResourceType,
WorkflowModelModeratedInvitation.wfVarTemplateAssetsUrl,
WorkflowModelModeratedInvitation.wfVarWorkspaceUrl);
/**
@@ -172,6 +175,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private MessageService messageService;
private InviteNominatedSender inviteNominatedSender;
private InviteModeratedSender inviteModeratedSender;
private ClientAppConfig clientAppConfig;
// maximum number of tries to generate a invitee user name which
// does not already belong to an existing person
@@ -194,6 +198,11 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private String moderatedInvitationWorkflowId =
WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME_ACTIVITI;
public void setClientAppConfig(ClientAppConfig clientAppConfig)
{
this.clientAppConfig = clientAppConfig;
}
/**
* Set the policy component
*
@@ -252,6 +261,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
PropertyCheck.mandatory(this, "PasswordGenerator", passwordGenerator);
PropertyCheck.mandatory(this, "PolicyComponent", policyComponent);
PropertyCheck.mandatory(this, "templateService", templateService);
PropertyCheck.mandatory(this, "clientAppConfig", clientAppConfig);
this.inviteNominatedSender = new InviteNominatedSender(serviceRegistry, repositoryHelper, messageService);
this.inviteModeratedSender = new InviteModeratedSender(serviceRegistry, repositoryHelper, messageService);
@@ -2042,4 +2052,71 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
{
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)
{
if (resourceType == Invitation.ResourceType.WEB_SITE)
{
return startModeratedInvite(inviteeComments, inviteeUserName, resourceType, resourceName, inviteeRole, clientName);
}
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)
{
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());
workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getProperty("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_RESOURCE_NAME;
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.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(CREATED_AT,(Date)props.get(ContentModel.PROP_CREATED));
parentProps.put(MODIFIED_AT, (Date)props.get(WF_PROP_MODIFIED_AT));
parentProps.put(CLIENT_NAME, (String)props.get(WF_PROP_CLIENT_NAME));
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_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
public static final String wfVarInviteeUserName = "imwf_inviteeUserName";
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 wfVarReviewComments = "imwf_reviewComments";
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 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.util.EmailHelper;
/**
* 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 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 EMAIL_SUBJECT_KEY =
"invitation.moderated.email.subject";
public static final String EMAIL_SUBJECT_KEY = "invitation.moderated.email.subject";
private static final String EMAIL_TEMPLATE_REF ="alfresco/templates/workspace/invite-email-moderated.html.ftl";
public void setEmailTemplatePath(String emailTemplatePath)
@@ -50,11 +55,33 @@ public class SendModeratedInviteDelegate extends AbstractInvitationDelegate
this.emailTemplatePath = emailTemplatePath;
}
public void setClientAppConfig(ClientAppConfig clientAppConfig)
{
this.clientAppConfig = clientAppConfig;
}
public void setEmailHelper(EmailHelper emailHelper)
{
this.emailHelper = emailHelper;
}
@Override
public void execute(DelegateExecution execution) throws Exception
{
String invitationId = ActivitiConstants.ENGINE_ID + "$" + execution.getProcessInstanceId();
Map<String, Object> variables = execution.getVariables();
String clientName = (String) variables.get(WorkflowModelModeratedInvitation.wfVarClientName);
if(clientName != null && clientAppConfig.getClient(clientName) != null)
{
ClientAppConfig.ClientApp clientApp = clientAppConfig.getClient(clientName);
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);
}
}
}

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.security.PersonService.PersonInfo;
import org.alfresco.util.ModelUtil;
import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
@@ -60,6 +61,7 @@ public class InviteModeratedSender extends InviteSender
private static final String DATA_DICTIONARY_XPATH_PREFIX = "app:";
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 WORKSPACE_PENDING_INVITES_LINK = "{0}/#/{1}/members/libraries";
private static final List<String> INVITE_MODERATED_EXPECTED_PROPERTIES = Arrays.asList(
WorkflowModelModeratedInvitation.wfVarInviteeUserName,
@@ -107,6 +109,8 @@ public class InviteModeratedSender extends InviteSender
model.put("inviteeName", StringUtils.join(new String[] { inviteePerson.getFirstName(), inviteePerson.getLastName() }, " "));
model.put("siteName", getSiteName(properties));
model.put("sharePendingInvitesLink", StringUtils.stripStart(getPendingInvitesLink(properties), "/"));
model.put("workspacePendingInvitesLink", StringUtils.stripStart(getWorkSpaceInvitesLink(properties), "/"));
model.put("template_assets_url", getTemplateAssetsLink(properties));
return model;
}
@@ -116,6 +120,24 @@ public class InviteModeratedSender extends InviteSender
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
public List<String> getRequiredProperties()
{

View File

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

View File

@@ -97,4 +97,10 @@ public interface Invitation
Date getCreatedAt();
Date getModifiedAt();
/**
* Which client to be sent
* @return the clientName
*/
String getClientName();
}

View File

@@ -138,6 +138,18 @@ public interface InvitationService
@Auditable(parameters = { "inviteeComments", "inviteeUserName", "resourceType", "resourceName", "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
*/
@Auditable(parameters = { "inviteeComments", "inviteeUserName", "resourceType", "resourceName", "inviteeRole", "clientName" })
public ModeratedInvitation inviteModerated(String inviteeComments, String inviteeUserName, Invitation.ResourceType resourceType, String resourceName, String inviteeRole, String clientName);
/**
* Update the invitee comments for an existing moderated invitation
*

View File

@@ -39,7 +39,8 @@ public class UrlUtil
{
// ${shareUrl} placeholder
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
* {@link SysAdminParams}.
@@ -113,6 +114,15 @@ public class UrlUtil
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)
{
StringBuilder url = new StringBuilder();

View File

@@ -47,7 +47,6 @@
<import resource="classpath:alfresco/notification-services-context.xml"/>
<import resource="classpath:alfresco/activities/activities-feed-context.xml" />
<import resource="classpath:alfresco/tagging-services-context.xml"/>
<import resource="classpath:alfresco/invitation-service-context.xml"/>
<import resource="classpath:alfresco/webdav-context.xml"/>
<import resource="classpath*:alfresco/patch/*-context.xml" />
<import resource="classpath*:alfresco/dbscripts/*-context.xml" />

View File

@@ -44,4 +44,5 @@
<import resource="classpath*:alfresco/deprecated-model-bootstrap-context.xml" />
<import resource="classpath*:alfresco/events-context.xml" />
<import resource="classpath*:alfresco/messaging-context.xml" />
<import resource="classpath:alfresco/invitation-service-context.xml"/>
</beans>

View File

@@ -18,3 +18,8 @@ repo.client-app.share.requestResetPasswordTemplatePath=
repo.client-app.share.resetPasswordPageUrl=${shareUrl}/page/reset-password
# reset password confirmation email template path
repo.client-app.share.confirmResetPasswordTemplatePath=
### Digital workspace template configurations
repo.client-app.workspace.inviteModeratedTemplatePath=
repo.client-app.workspace.workspaceUrl=workspace
repo.client-app.workspace.templateAssetsUrl=${alfrescoUrl}/images

View File

@@ -33,6 +33,7 @@
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="repositoryHelper" ref="repositoryHelper"/>
<property name="messageService" ref="messageService"/>
<property name="clientAppConfig" ref="clientAppConfig"/>
</bean>
<!-- Site service security bean -->
@@ -119,5 +120,7 @@
<property name="emailTemplatePath">
<util:constant static-field="org.alfresco.repo.invitation.activiti.SendModeratedInviteDelegate.ENTERPRISE_EMAIL_TEMPLATE_PATH"/>
</property>
<property name="clientAppConfig" ref="clientAppConfig"/>
<property name="emailHelper" ref="emailHelper"/>
</bean>
</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.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,143 @@
<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 0 0 0">
<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: 60px;" width="100%">
<tbody>
<tr>
<td style="padding-bottom:40px;padding-left:40px;">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td height="30" width="30">
<img src="${template_assets_url}/logo/workspace.png" alt="Alfresco" width="35" height="35" style="border:none;">
</td>
<td align="" valign="center" bgcolor="#FFFFFF">
<span bgcolor="#FFFFFF" style="font-size: 16px;letter-spacing: 0;color: #212328;opacity: 1;padding: 10px;" align="left">
Digital Workspace
</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
<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 style="padding-right:40px;padding-bottom:40px;padding-left:40px">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<p style="letter-spacing: 0; color: #707070;font-size: 28px; margin: 0">
Join Request
</p>
</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: 0; color: #707070; display: block; font-weight: bold; font-size: 20px; margin: 0;">
${inviteeName}
</p>
<p style="letter-spacing: 0;color: #707070;margin: 0;font-size: 20px">
${message("templates.workspace.invite-email-moderated.html.body")}
</p>
<p style="letter-spacing: 0;color: #707070;display: block;font-weight: bold;font-size: 20px;margin: 0;">
${siteName}
</p>
<p style="letter-spacing: 0; color: #707070; display: block; margin: 40px 0">
<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: 18px; font-family:Helvetica,Arial,Verdana,sans-serif;font-weight:400;padding: 15px 35px;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">
<type>d:date</type>
</property>
<property name="imwf:clientName">
<type>d:text</type>
</property>
</properties>
</aspect>
</aspects>

View File

@@ -65,6 +65,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;
@@ -202,9 +204,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
* end of setup now for some real tests
*/
/**
*
*/
@Test
public void testConfiguration()
{
assertNotNull("Invitation service is null", invitationService);
@@ -216,6 +216,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void testInternalUserNotDeletedAfterInviteCancelled() throws Exception
{
// Disable our existing User
@@ -257,6 +258,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void testExternalUserDeletedAfterInviteCancelled() throws Exception
{
String inviteeFirstName = PERSON_FIRSTNAME;
@@ -290,6 +292,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void testNominatedInvitationNewUser() throws Exception
{
Calendar calendar = Calendar.getInstance();
@@ -467,6 +470,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void testNominatedInvitationNewUserReject() throws Exception
{
Calendar calendar = Calendar.getInstance();
@@ -543,6 +547,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void testNominatedInvitationNewUserSameEmails() throws Exception
{
String inviteeAFirstName = "John";
@@ -624,6 +629,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
siteService.removeMembership(resourceName, inviteeBUserName);
}
@Test
public void testMNT11775() throws Exception
{
String inviteeUserName = USER_TWO;
@@ -693,6 +699,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
*
* @throws Exception
*/
@Test
public void test_MNT15614() throws Exception
{
String[] siteNames = {"it", "site", "GROUP"};
@@ -726,6 +733,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
* Test nominated user - new user with whitespace in name. Related to
* ETHREEOH-3030.
*/
@Test
public void testNominatedInvitationNewUserWhitespace() throws Exception
{
String inviteeFirstName = PERSON_FIRSTNAME_SPACES;
@@ -919,6 +927,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
siteService.removeMembership(resourceName, inviteeUserName);
}
@Test
public void testNominatedInvitationExistingUser() throws Exception
{
this.invitationServiceImpl.setNominatedInvitationWorkflowId(
@@ -931,6 +940,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
* moderated invitation Reject the invitation Create a moderated invitation
* Approve the invitation
*/
@Test
public void testModeratedInvitation()
{
String inviteeUserName = USER_TWO;
@@ -1018,9 +1028,90 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
}
/**
* Create a moderated invitation for workspace client Get it Search for it Cancel it
*/
@Test
public void testWorkspaceModeratedInvitation()
{
String inviteeUserName = USER_TWO;
Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE;
String resourceName = SITE_SHORT_NAME_INVITE;
String inviteeRole = SiteModel.SITE_COLLABORATOR;
String comments = "please sir, let me in!";
this.authenticationComponent.setCurrentUser(USER_TWO);
ModeratedInvitation invitation = invitationService.inviteModerated(comments, inviteeUserName, resourceType,
resourceName, inviteeRole, "workspace");
assertNotNull("moderated invitation is null", invitation);
String inviteId = invitation.getInviteId();
assertEquals("user name wrong", inviteeUserName, invitation.getInviteeUserName());
assertEquals("role name wrong", inviteeRole, invitation.getRoleName());
assertEquals("comments", comments, invitation.getInviteeComments());
assertEquals("resource type name wrong", resourceType, invitation.getResourceType());
assertEquals("resource name wrong", resourceName, invitation.getResourceName());
assertEquals("client name wrong", "workspace", invitation.getClientName());
/**
* Now we have an invitation get it and check the details have been
* returned correctly.
*/
ModeratedInvitation mi2 = (ModeratedInvitation) invitationService.getInvitation(inviteId);
assertEquals("invite id", inviteId, mi2.getInviteId());
assertEquals("user name wrong", inviteeUserName, mi2.getInviteeUserName());
assertEquals("role name wrong", inviteeRole, mi2.getRoleName());
assertEquals("comments", comments, mi2.getInviteeComments());
assertEquals("resource type name wrong", resourceType, mi2.getResourceType());
assertEquals("resource name wrong", resourceName, mi2.getResourceName());
/**
* Search for the new invitation
*/
List<Invitation> invitations = invitationService.listPendingInvitationsForResource(resourceType, resourceName);
assertTrue("invitations is empty", !invitations.isEmpty());
ModeratedInvitation firstInvite = (ModeratedInvitation) invitations.get(0);
assertEquals("invite id wrong", inviteId, firstInvite.getInviteId());
/**
* Cancel the invitation
*/
ModeratedInvitation canceledInvitation = (ModeratedInvitation) invitationService.cancel(inviteId);
assertEquals("invite id wrong", inviteId, canceledInvitation.getInviteId());
assertEquals("comments wrong", comments, canceledInvitation.getInviteeComments());
/**
* Should now be no invitation
*/
List<Invitation> inv2 = invitationService.listPendingInvitationsForResource(resourceType, resourceName);
assertTrue("After cancel invitations is not empty", inv2.isEmpty());
/**
* New invitation
*/
this.authenticationComponent.setCurrentUser(USER_TWO);
ModeratedInvitation invite3 = invitationService.inviteModerated(comments, inviteeUserName, resourceType,
resourceName, inviteeRole);
assertEquals("client name wrong", null, invite3.getClientName());
String thirdInvite = invite3.getInviteId();
this.authenticationComponent.setCurrentUser(USER_MANAGER);
invitationService.approve(thirdInvite, "Welcome in");
/**
* Now verify access control list
*/
String roleName = siteService.getMembersRole(resourceName, inviteeUserName);
assertEquals("role name wrong", inviteeRole, roleName);
siteService.removeMembership(resourceName, inviteeUserName);
}
/**
* Test the approval of a moderated invitation
*/
@Test
public void testModeratedApprove()
{
String inviteeUserName = USER_TWO;
@@ -1090,6 +1181,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
/**
* Tests of Moderated Reject
*/
@Test
public void testModeratedReject()
{
String inviteeUserName = USER_TWO;
@@ -1148,6 +1240,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
/**
* Test search invitation
*/
@Test
public void testSearchInvitation()
{
/**
@@ -1240,6 +1333,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
/**
* test that the search limiter works
*/
@Test
public void testSearchInvitationWithLimit() throws Exception
{
Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE;
@@ -1282,6 +1376,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
/**
* MNT-17341 : External users with Manager role cannot invite other external users to the site because site invitation accept fails
*/
@Test
public void testExternalUserManagerInvitingAnotherExternalUser() throws Exception{
String inviteeFirstName = PERSON_FIRSTNAME;
String inviteeLastName = PERSON_LASTNAME;
@@ -1334,6 +1429,8 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
}
@Ignore
@Test
@Commit
public void disabled_test100Invites() throws Exception
{
@@ -1367,6 +1464,7 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri
assertEquals(invite.getInviteId(), results.get(0).getInviteId());
}
@Test
public void testGetInvitation()
{
try

View File

@@ -44,6 +44,8 @@ import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.admin.SysAdminParamsImpl;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.invitation.WorkflowModelModeratedInvitation;
import org.alfresco.repo.invitation.activiti.SendModeratedInviteDelegate;
@@ -175,6 +177,7 @@ public class InviteModeratedSenderTest extends TestCase
SearchService mockSearchService = mockSearchService();
SiteService mockSiteService = mockSiteService();
FileFolderService mockFileFolderService = mockFileFolderService();
SysAdminParams sysAdminParams = new SysAdminParamsImpl();
ServiceRegistry services = mock(ServiceRegistry.class);
when(services.getActionService()).thenReturn(mockActionService);
@@ -183,6 +186,7 @@ public class InviteModeratedSenderTest extends TestCase
when(services.getSearchService()).thenReturn(mockSearchService);
when(services.getSiteService()).thenReturn(mockSiteService);
when(services.getFileFolderService()).thenReturn(mockFileFolderService);
when(services.getSysAdminParams()).thenReturn(sysAdminParams);
return services;
}