From 29945e32051a2a5ae935cdfe9828f667ea341191 Mon Sep 17 00:00:00 2001 From: Alexandru Epure Date: Tue, 9 Aug 2016 14:07:57 +0000 Subject: [PATCH] Merged 5.2.N (5.2.1) to HEAD (5.2) 129048 cpopa: SHA-1627 : CLONE - Create email template - Hooked a new step into the invite moderated workflow, so as to notify by email all site managers of site join requests. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@129310 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/import-export-context.xml | 6 + .../alfresco/invitation-service-context.xml | 3 + .../messages/bootstrap-spaces.properties | 2 + .../messages/invitation-service.properties | 2 + .../messages/templates-messages.properties | 7 + .../invite-email-moderated.html.ftl | 80 ++++++ .../invite-moderated-email-templates.xml | 25 ++ .../workflow/invitation-moderated.bpmn20.xml | 14 +- .../invitation/InvitationServiceImpl.java | 61 +++- .../WorkflowModelModeratedInvitation.java | 3 +- .../activiti/SendModeratedInviteDelegate.java | 52 ++++ .../site/InviteModeratedSender.java | 118 ++++++++ .../site/InviteNominatedSender.java | 240 ++++++++++++++++ .../repo/invitation/site/InviteSender.java | 265 ++++------------- .../cmr/invitation/InvitationService.java | 17 +- .../org/alfresco/AllUnitTestsSuite.java | 3 +- .../site/InviteModeratedSenderTest.java | 269 ++++++++++++++++++ .../invitation/site/InviteSenderTest.java | 8 +- 18 files changed, 929 insertions(+), 246 deletions(-) create mode 100644 config/alfresco/templates/invite-email-templates/invite-email-moderated.html.ftl create mode 100644 config/alfresco/templates/invite-moderated-email-templates.xml create mode 100644 source/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java create mode 100644 source/java/org/alfresco/repo/invitation/site/InviteModeratedSender.java create mode 100644 source/java/org/alfresco/repo/invitation/site/InviteNominatedSender.java create mode 100644 source/test-java/org/alfresco/repo/invitation/site/InviteModeratedSenderTest.java diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index 6d6d1f7da4..3bba4d0416 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -652,6 +652,12 @@ alfresco/templates/invite-add-direct-email-templates.xml alfresco/messages/bootstrap-spaces + + + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/${spaces.templates.email.invite.childname} + alfresco/templates/invite-moderated-email-templates.xml + alfresco/messages/bootstrap-spaces + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/${spaces.templates.email.notify.childname} diff --git a/config/alfresco/invitation-service-context.xml b/config/alfresco/invitation-service-context.xml index 3fa9e6c854..21cadcfcb0 100644 --- a/config/alfresco/invitation-service-context.xml +++ b/config/alfresco/invitation-service-context.xml @@ -108,5 +108,8 @@ + + diff --git a/config/alfresco/messages/bootstrap-spaces.properties b/config/alfresco/messages/bootstrap-spaces.properties index ae9670131a..62792f713e 100644 --- a/config/alfresco/messages/bootstrap-spaces.properties +++ b/config/alfresco/messages/bootstrap-spaces.properties @@ -104,6 +104,8 @@ spaces.templates.email.notify.description=Notify Email Templates spaces.templates.email.generate_the_invite_email.description=Email template used to generate the invite email for Alfresco Share spaces.templates.email.generate_the_invite_email_add_direct.description=Email template used to generate the user added to site email for Alfresco Share +spaces.templates.email.generate_the_invite_email_moderated.description=Email template used to generate the request to join site email for Alfresco Share + spaces.templates.email.generate_the_wf_notification_email.description=Email template for notifying users of new a workflow task email.template.email_template_for_notifying_users=Email template for notifying users from a rule or action diff --git a/config/alfresco/messages/invitation-service.properties b/config/alfresco/messages/invitation-service.properties index 2d73697758..3e274df577 100644 --- a/config/alfresco/messages/invitation-service.properties +++ b/config/alfresco/messages/invitation-service.properties @@ -23,6 +23,8 @@ invitation.invitesender.email.role.SiteConsumer=Site Consumer invitation.invitesender.emailAddDirect.subject=Alfresco {0}: You have been added to the {1} site +invitation.moderated.email.subject=A new user wants to join {0} + # Invitation workflow task description invitation.nominated.workflow.description=Invitation to join {0} site invitation.moderated.workflow.description=Request to join {0} site diff --git a/config/alfresco/messages/templates-messages.properties b/config/alfresco/messages/templates-messages.properties index 0c8910278b..f50c627b2e 100644 --- a/config/alfresco/messages/templates-messages.properties +++ b/config/alfresco/messages/templates-messages.properties @@ -84,3 +84,10 @@ templates.invite-email-add-direct.html.navigate=Go to {0} templates.invite-email-add-direct.html.closing=Sincerely,
Alfresco {0} templates.invite-email-add-direct.html.leave=Don''t want to be a part of this site?
Leave site templates.invite-email-add-direct.html.alfresco_info=To find out more about Alfresco {0} visit http://www.alfresco.com + +#invite-email-moderated.ftl +templates.invite-email-moderated.html.header=A new user wants to join ''{0}'' +templates.invite-email-moderated.html.info={0} requested to join the ''{1}'' site on {2}. +templates.invite-email-moderated.html.see_pending=See Pending Requests + +templates.invite-email.html.salutation_generic=Hi, diff --git a/config/alfresco/templates/invite-email-templates/invite-email-moderated.html.ftl b/config/alfresco/templates/invite-email-templates/invite-email-moderated.html.ftl new file mode 100644 index 0000000000..c971d0b113 --- /dev/null +++ b/config/alfresco/templates/invite-email-templates/invite-email-moderated.html.ftl @@ -0,0 +1,80 @@ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ + +
+ ${message("templates.invite-email-moderated.html.header", siteName)} +
+
+ ${date?datetime?string.full} +
+
+
+

${message("templates.invite-email.html.salutation_generic")}

+ +

${message("templates.invite-email-moderated.html.info", inviteeName, siteName, date?string["dd MMM yyyy"])}

+ + +
+ ${message("templates.invite-email-moderated.html.see_pending")} +
+
+ +

${message("templates.invite-email-add-direct.html.closing", productName!"")}

+ +
+
+
+
 
+
+ ${message("templates.invite-email-add-direct.html.alfresco_info", productName!"")} +
+
 
+
+ +
+
+
+ + \ No newline at end of file diff --git a/config/alfresco/templates/invite-moderated-email-templates.xml b/config/alfresco/templates/invite-moderated-email-templates.xml new file mode 100644 index 0000000000..19c38f385b --- /dev/null +++ b/config/alfresco/templates/invite-moderated-email-templates.xml @@ -0,0 +1,25 @@ + + + + /app:company_home/app:dictionary/app:email_templates/cm:invite + + + + + + + + + + + + + true + invite-email-moderated.html.ftl + invite-email-moderated.html.ftl + ${spaces.templates.email.generate_the_invite_email_moderated.description} - ${version.default} + contentUrl=classpath:alfresco/templates/invite-email-templates/invite-email-moderated.html.ftl|mimetype=text/plain|size=5301|encoding=UTF-8 + + + + diff --git a/config/alfresco/workflow/invitation-moderated.bpmn20.xml b/config/alfresco/workflow/invitation-moderated.bpmn20.xml index ae7a66dc83..f67a698e0f 100644 --- a/config/alfresco/workflow/invitation-moderated.bpmn20.xml +++ b/config/alfresco/workflow/invitation-moderated.bpmn20.xml @@ -7,8 +7,12 @@ + + + + - + @@ -41,21 +45,21 @@ - + - + ${imwf_reviewOutcome=='approve'} - + - + diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java index e025d66091..5aa002d1b8 100644 --- a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java +++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java @@ -35,7 +35,7 @@ import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVa import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarResourceName; import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRole; import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarServerPath; - + import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -47,13 +47,15 @@ import java.util.List; import java.util.Locale; import java.util.Map; 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.i18n.MessageService; import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate; -import org.alfresco.repo.invitation.site.InviteSender; +import org.alfresco.repo.invitation.site.InviteNominatedSender; +import org.alfresco.repo.invitation.site.InviteModeratedSender; +import org.alfresco.repo.invitation.site.InviteSender; import org.alfresco.repo.jscript.ScriptNode; import org.alfresco.repo.model.Repository; import org.alfresco.repo.node.NodeServicePolicies; @@ -126,7 +128,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli private static final Log logger = LogFactory.getLog(InvitationServiceImpl.class); private static final String REJECT_TEMPLATE = "/alfresco/bootstrap/invite/moderated-reject-email.ftl"; private static final String MSG_NOT_SITE_MANAGER = "invitation.cancel.not_site_manager"; - private static final Collection sendInvitePropertyNames = Arrays.asList(wfVarInviteeUserName,// + private static final List SEND_INVITE_NOMINATED_PROP_NAMES = Arrays.asList(wfVarInviteeUserName,// wfVarResourceName,// wfVarInviterUserName,// wfVarInviteeUserName,// @@ -137,7 +139,15 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli wfVarServerPath,// wfVarAcceptUrl,// wfVarRejectUrl, - InviteSender.WF_INSTANCE_ID); + InviteNominatedSender.WF_INSTANCE_ID); + + private static final List SEND_INVITE_MODERATED_PROP_NAMES = Arrays.asList( + WorkflowModelModeratedInvitation.wfVarInviteeUserName, + WorkflowModelModeratedInvitation.wfVarInviteeRole, + WorkflowModelModeratedInvitation.wfVarResourceName, + WorkflowModelModeratedInvitation.bpmGroupAssignee, + WorkflowModelModeratedInvitation.wfVarResourceType); + /** * Services @@ -161,7 +171,8 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli private Repository repositoryHelper; private ServiceRegistry serviceRegistry; private MessageService messageService; - private InviteSender inviteSender; + private InviteNominatedSender inviteNominatedSender; + private InviteModeratedSender inviteModeratedSender; // maximum number of tries to generate a invitee user name which // does not already belong to an existing person @@ -243,7 +254,8 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli PropertyCheck.mandatory(this, "PolicyComponent", policyComponent); PropertyCheck.mandatory(this, "templateService", templateService); - this.inviteSender = new InviteSender(serviceRegistry, repositoryHelper, messageService); + this.inviteNominatedSender = new InviteNominatedSender(serviceRegistry, repositoryHelper, messageService); + this.inviteModeratedSender = new InviteModeratedSender(serviceRegistry, repositoryHelper, messageService); // this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), @@ -2011,20 +2023,39 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli public void sendNominatedInvitation(String inviteId, String emailTemplateXpath, String emailSubjectKey, Map executionVariables) { + sendInviteEmail(inviteNominatedSender, SEND_INVITE_NOMINATED_PROP_NAMES, inviteId, emailTemplateXpath, emailSubjectKey, executionVariables); + } + + private void sendInviteEmail(InviteSender inviteSender, List invitePropNames, String inviteId, String emailTemplateXpath, String emailSubjectKey, Map executionVariables) + { if (isSendEmails()) { - Map properties = makePropertiesFromContextVariables(executionVariables, sendInvitePropertyNames); + Map properties = makePropertiesFromContextVariables(executionVariables, invitePropNames); - String packageName = WorkflowModel.ASSOC_PACKAGE.toPrefixString(namespaceService).replace(":", "_"); - ScriptNode packageNode = (ScriptNode) executionVariables.get(packageName); - String packageRef = packageNode.getNodeRef().toString(); - properties.put(InviteSender.WF_PACKAGE, packageRef); + String packageRef = getPackageRef(executionVariables); + properties.put(InviteNominatedSender.WF_PACKAGE, packageRef); - properties.put(InviteSender.WF_INSTANCE_ID, inviteId); + properties.put(InviteNominatedSender.WF_INSTANCE_ID, inviteId); inviteSender.sendMail(emailTemplateXpath, emailSubjectKey, properties); - } - } + } + } + + + @Override + public void sendModeratedInvitation(String inviteId, String emailTemplateXpath, String emailSubjectKey, Map executionVariables) + { + sendInviteEmail(inviteModeratedSender, SEND_INVITE_MODERATED_PROP_NAMES, inviteId, emailTemplateXpath, emailSubjectKey, executionVariables); + + } + + private String getPackageRef(Map executionVariables) + { + String packageName = WorkflowModel.ASSOC_PACKAGE.toPrefixString(namespaceService).replace(":", "_"); + ScriptNode packageNode = (ScriptNode) executionVariables.get(packageName); + String packageRef = packageNode.getNodeRef().toString(); + return packageRef; + } @Override public void cancelInvitation(String siteName, String invitee, String inviteId, String currentInviteId) diff --git a/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java b/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java index 215db9f905..bf5ca9c6b8 100644 --- a/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java +++ b/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java @@ -75,5 +75,6 @@ public interface WorkflowModelModeratedInvitation public static final String wfVarResourceName = "imwf_resourceName"; public static final String wfVarResourceType = "imwf_resourceType"; 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"; } diff --git a/source/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java b/source/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java new file mode 100644 index 0000000000..006dc280d9 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java @@ -0,0 +1,52 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.invitation.activiti; + +import java.util.Map; + +import org.activiti.engine.delegate.DelegateExecution; +import org.alfresco.repo.workflow.activiti.ActivitiConstants; + +/** + * Activiti delegate that is executed when a invitation request has been sent. + * + * @author Constantin Popa + */ +public class SendModeratedInviteDelegate extends AbstractInvitationDelegate +{ + public static final String EMAIL_TEMPLATE_XPATH = + "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"; + + @Override + public void execute(DelegateExecution execution) throws Exception + { + String invitationId = ActivitiConstants.ENGINE_ID + "$" + execution.getProcessInstanceId(); + Map variables = execution.getVariables(); + invitationService.sendModeratedInvitation(invitationId, EMAIL_TEMPLATE_XPATH, EMAIL_SUBJECT_KEY, variables); + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/InviteModeratedSender.java b/source/java/org/alfresco/repo/invitation/site/InviteModeratedSender.java new file mode 100644 index 0000000000..6352aa7817 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/InviteModeratedSender.java @@ -0,0 +1,118 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.invitation.site; + +import static org.alfresco.repo.invitation.WorkflowModelModeratedInvitation.bpmGroupAssignee; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.executer.MailActionExecuter; +import org.alfresco.repo.i18n.MessageService; +import org.alfresco.repo.invitation.WorkflowModelModeratedInvitation; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.Action; +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.apache.commons.lang3.StringUtils; + + +/** + * This class is responsible for sending email notifications to site managers informing about users requesting access. + * + * @author Constantin Popa + */ +public class InviteModeratedSender extends InviteSender +{ + public static final String WF_PACKAGE = "wf_package"; + public static final String SHARE_PENDING_INVITES_LINK = "page/site/{0}/pending-invites"; + + private static final List INVITE_MODERATED_EXPECTED_PROPERTIES = Arrays.asList( + WorkflowModelModeratedInvitation.wfVarInviteeUserName, + WorkflowModelModeratedInvitation.wfVarInviteeRole, + WorkflowModelModeratedInvitation.wfVarResourceName, + WorkflowModelModeratedInvitation.bpmGroupAssignee, + WorkflowModelModeratedInvitation.wfVarResourceType); + + + public InviteModeratedSender(ServiceRegistry services, Repository repository, MessageService messageService) + { + super(services, repository, messageService); + } + + @Override + public void sendMail(String emailTemplateXpath, String emailSubjectKey, Map properties) + { + checkProperties(properties); + NodeRef invitee = personService.getPerson(properties.get(WorkflowModelModeratedInvitation.wfVarInviteeUserName)); + Action mail = actionService.createAction(MailActionExecuter.NAME); + mail.setParameterValue(MailActionExecuter.PARAM_FROM, (String) nodeService.getProperty(invitee, ContentModel.PROP_EMAIL)); + mail.setParameterValue(MailActionExecuter.PARAM_TO_MANY, properties.get(bpmGroupAssignee)); + mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, emailSubjectKey); + mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT_PARAMS, new Object[] { getSiteName(properties) }); + mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, getEmailTemplateNodeRef(emailTemplateXpath)); + mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable) buildMailTextModel(properties)); + mail.setParameterValue(MailActionExecuter.PARAM_IGNORE_SEND_FAILURE, true); + actionService.executeAction(mail, new NodeRef(properties.get(WF_PACKAGE))); + } + + @Override + protected Map buildMailTextModel(Map properties) + { + NodeRef invitee = personService.getPerson(properties.get(WorkflowModelModeratedInvitation.wfVarInviteeUserName)); + Map model = new HashMap(); + model.put(TemplateService.KEY_COMPANY_HOME, repository.getCompanyHome()); + model.put(TemplateService.KEY_USER_HOME, repository.getUserHome(repository.getPerson())); + model.put(TemplateService.KEY_PRODUCT_NAME, ModelUtil.getProductName(repoAdminService)); + + PersonInfo inviteePerson = personService.getPerson(invitee); + model.put("inviteeName", StringUtils.join(new String[] { inviteePerson.getFirstName(), inviteePerson.getLastName() }, " ")); + model.put("siteName", getSiteName(properties)); + model.put("sharePendingInvitesLink", MessageFormat.format(SHARE_PENDING_INVITES_LINK, properties.get(WorkflowModelModeratedInvitation.wfVarResourceName))); + return model; + } + + @Override + public List getRequiredProperties() + { + return INVITE_MODERATED_EXPECTED_PROPERTIES; + } + + @Override + protected String getWorkflowPropForSiteName() + { + return WorkflowModelModeratedInvitation.wfVarResourceName; + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/InviteNominatedSender.java b/source/java/org/alfresco/repo/invitation/site/InviteNominatedSender.java new file mode 100644 index 0000000000..3ba374f366 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/InviteNominatedSender.java @@ -0,0 +1,240 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.invitation.site; + +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarAcceptUrl; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteTicket; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeGenPassword; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeUserName; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviterUserName; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRejectUrl; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarResourceName; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRole; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarServerPath; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.executer.MailActionExecuter; +import org.alfresco.repo.i18n.MessageService; +import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.TemplateService; +import org.alfresco.util.ModelUtil; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.extensions.surf.util.URLEncoder; + +/** + * This class is responsible for sending email invitations, allowing nominated + * user's to join a Site. + * + * @author Nick Smith + */ +public class InviteNominatedSender extends InviteSender +{ + public static final String WF_INSTANCE_ID = "wf_instanceId"; + public static final String WF_PACKAGE = "wf_package"; + public static final String SITE_LEAVE_HASH = "#leavesite"; + private static final String SITE_DASHBOARD_ENDPOINT_PATTERN = "/page/site/{0}/dashboard"; + + private static final List INVITE_NOMINATED_EXPECTED_PROPERTIES = Arrays.asList(wfVarInviteeUserName,// + wfVarResourceName,// + wfVarInviterUserName,// + wfVarInviteeUserName,// + wfVarRole,// + wfVarInviteeGenPassword,// + wfVarResourceName,// + wfVarInviteTicket,// + wfVarServerPath,// + wfVarAcceptUrl,// + wfVarRejectUrl, WF_INSTANCE_ID,// + WF_PACKAGE); + + public InviteNominatedSender(ServiceRegistry services, Repository repository, MessageService messageService) + { + super(services, repository, messageService); + } + + /** + * Implemented for backwards compatibility + * + * @param properties + * @deprecated + * @see {@link #sendMail(String, String, Map)} + */ + public void sendMail(Map properties) + { + sendMail(SendNominatedInviteDelegate.EMAIL_TEMPLATE_XPATH, SendNominatedInviteDelegate.EMAIL_SUBJECT_KEY, properties); + } + + @Override + public void sendMail(String emailTemplateXpath, String emailSubjectKey, Map properties) + { + checkProperties(properties); + ParameterCheck.mandatory("Properties", properties); + NodeRef inviter = personService.getPerson(properties.get(wfVarInviterUserName)); + String inviteeName = properties.get(wfVarInviteeUserName); + NodeRef invitee = personService.getPerson(inviteeName); + Action mail = actionService.createAction(MailActionExecuter.NAME); + mail.setParameterValue(MailActionExecuter.PARAM_FROM, getEmail(inviter)); + mail.setParameterValue(MailActionExecuter.PARAM_TO, getEmail(invitee)); + mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, emailSubjectKey); + mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT_PARAMS, new Object[] { ModelUtil.getProductName(repoAdminService), getSiteName(properties) }); + mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, getEmailTemplateNodeRef(emailTemplateXpath)); + mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable) buildMailTextModel(properties)); + mail.setParameterValue(MailActionExecuter.PARAM_IGNORE_SEND_FAILURE, true); + actionService.executeAction(mail, getWorkflowPackage(properties)); + } + + @Override + protected Map buildMailTextModel(Map properties) + { + NodeRef inviter = personService.getPerson(properties.get(wfVarInviterUserName)); + NodeRef invitee = personService.getPerson(properties.get(wfVarInviteeUserName)); + // Set the core model parts + // Note - the user part is skipped, as that's implied via the run-as + Map model = new HashMap(); + model.put(TemplateService.KEY_COMPANY_HOME, repository.getCompanyHome()); + model.put(TemplateService.KEY_USER_HOME, repository.getUserHome(repository.getPerson())); + model.put(TemplateService.KEY_PRODUCT_NAME, ModelUtil.getProductName(repoAdminService)); + + // Build up the args for rendering inside the template + Map args = buildArgs(properties, inviter, invitee); + model.put("args", (Serializable) args); + + // All done + return model; + } + + private Map buildArgs(Map properties, NodeRef inviter, NodeRef invitee) + { + String params = buildUrlParamString(properties); + String acceptLink = makeLink(properties.get(wfVarServerPath), properties.get(wfVarAcceptUrl), params, null); + String rejectLink = makeLink(properties.get(wfVarServerPath), properties.get(wfVarRejectUrl), params, null); + + String siteDashboardEndpoint = getSiteDashboardEndpoint(properties); + String siteDashboardLink = makeLink(properties.get(wfVarServerPath), siteDashboardEndpoint, null, null); + String siteLeaveLink = makeLink(properties.get(wfVarServerPath), siteDashboardEndpoint, null, SITE_LEAVE_HASH); + + Map args = new HashMap(); + args.put("inviteePersonRef", invitee.toString()); + args.put("inviterPersonRef", inviter.toString()); + args.put("siteName", getSiteName(properties)); + args.put("inviteeSiteRole", getRoleName(properties)); + args.put("inviteeUserName", properties.get(wfVarInviteeUserName)); + args.put("inviteeGenPassword", properties.get(wfVarInviteeGenPassword)); + args.put("acceptLink", acceptLink); + args.put("rejectLink", rejectLink); + args.put("siteDashboardLink", siteDashboardLink); + args.put("siteLeaveLink", siteLeaveLink); + return args; + } + + protected String makeLink(String location, String endpoint, String queryParams, String hashParam) + { + location = location.endsWith("/") ? location : location + "/"; + endpoint = endpoint.startsWith("/") ? endpoint.substring(1) : endpoint; + if (queryParams != null) + { + queryParams = queryParams.startsWith("?") ? queryParams : "?" + queryParams; + } + else + { + queryParams = ""; + } + if (hashParam != null) + { + hashParam = hashParam.startsWith("#") ? hashParam : "#" + hashParam; + } + else + { + hashParam = ""; + } + return location + endpoint + queryParams + hashParam; + } + + private String getRoleName(Map properties) + { + String roleName = properties.get(wfVarRole); + String role = messageService.getMessage("invitation.invitesender.email.role." + roleName); + if (role == null) + { + role = roleName; + } + return role; + } + + private String getEmail(NodeRef person) + { + return (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); + } + + private NodeRef getWorkflowPackage(Map properties) + { + String packageRef = properties.get(WF_PACKAGE); + return new NodeRef(packageRef); + } + + private String buildUrlParamString(Map properties) + { + StringBuilder params = new StringBuilder("?inviteId="); + params.append(properties.get(WF_INSTANCE_ID)); + params.append("&inviteeUserName="); + params.append(URLEncoder.encode(properties.get(wfVarInviteeUserName))); + params.append("&siteShortName="); + params.append(properties.get(wfVarResourceName)); + params.append("&inviteTicket="); + params.append(properties.get(wfVarInviteTicket)); + return params.toString(); + } + + private String getSiteDashboardEndpoint(Map properties) + { + String siteName = properties.get(wfVarResourceName); + return MessageFormat.format(SITE_DASHBOARD_ENDPOINT_PATTERN, siteName); + } + + @Override + public List getRequiredProperties() + { + return INVITE_NOMINATED_EXPECTED_PROPERTIES; + } + + @Override + protected String getWorkflowPropForSiteName() + { + return wfVarResourceName; + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/InviteSender.java b/source/java/org/alfresco/repo/invitation/site/InviteSender.java index 4e0a988448..1caa5193a7 100644 --- a/source/java/org/alfresco/repo/invitation/site/InviteSender.java +++ b/source/java/org/alfresco/repo/invitation/site/InviteSender.java @@ -26,85 +26,46 @@ package org.alfresco.repo.invitation.site; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarAcceptUrl; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteTicket; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeGenPassword; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviteeUserName; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarInviterUserName; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRejectUrl; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarResourceName; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarRole; -import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarServerPath; - -import java.io.Serializable; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.action.executer.MailActionExecuter; -import org.alfresco.repo.i18n.MessageService; -import org.alfresco.repo.invitation.activiti.SendNominatedInviteDelegate; -import org.alfresco.repo.model.Repository; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.action.ActionService; -import org.alfresco.service.cmr.admin.RepoAdminService; -import org.alfresco.service.cmr.invitation.InvitationException; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.TemplateService; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.util.ModelUtil; -import org.springframework.extensions.surf.util.ParameterCheck; -import org.springframework.extensions.surf.util.URLEncoder; +import static org.alfresco.repo.invitation.WorkflowModelNominatedInvitation.wfVarResourceName; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.i18n.MessageService; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.admin.RepoAdminService; +import org.alfresco.service.cmr.invitation.InvitationException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespaceService; /** - * This class is responsible for sending email invitations, allowing nominated - * user's to join a Site. + * Notifies the necessary user(s) of a pending site membership invitation/request. * * @author Nick Smith */ -public class InviteSender +public abstract class InviteSender { - public static final String WF_INSTANCE_ID = "wf_instanceId"; - public static final String WF_PACKAGE = "wf_package"; - public static final String SITE_LEAVE_HASH = "#leavesite"; - private static final String SITE_DASHBOARD_ENDPOINT_PATTERN = "/page/site/{0}/dashboard"; - - private static final List expectedProperties = Arrays.asList(wfVarInviteeUserName,// - wfVarResourceName,// - wfVarInviterUserName,// - wfVarInviteeUserName,// - wfVarRole,// - wfVarInviteeGenPassword,// - wfVarResourceName,// - wfVarInviteTicket,// - wfVarServerPath,// - wfVarAcceptUrl,// - wfVarRejectUrl, WF_INSTANCE_ID,// - WF_PACKAGE); - - private final ActionService actionService; - private final NodeService nodeService; - private final PersonService personService; - private final SearchService searchService; - private final SiteService siteService; - private final Repository repository; - private final MessageService messageService; - private final FileFolderService fileFolderService; -// private final SysAdminParams sysAdminParams; - private final RepoAdminService repoAdminService; - private final NamespaceService namespaceService; + protected final ActionService actionService; + protected final NodeService nodeService; + protected final PersonService personService; + protected final SearchService searchService; + protected final SiteService siteService; + protected final Repository repository; + protected final MessageService messageService; + protected final FileFolderService fileFolderService; + protected final RepoAdminService repoAdminService; + protected final NamespaceService namespaceService; public InviteSender(ServiceRegistry services, Repository repository, MessageService messageService) { @@ -114,24 +75,11 @@ public class InviteSender this.searchService = services.getSearchService(); this.siteService = services.getSiteService(); this.fileFolderService = services.getFileFolderService(); -// this.sysAdminParams = services.getSysAdminParams(); this.repoAdminService = services.getRepoAdminService(); this.namespaceService = services.getNamespaceService(); this.repository = repository; this.messageService = messageService; } - - /** - * Implemented for backwards compatibility - * - * @param properties - * @deprecated - * @see {@link #sendMail(String, String, Map)} - */ - public void sendMail(Map properties) - { - sendMail(SendNominatedInviteDelegate.EMAIL_TEMPLATE_XPATH, SendNominatedInviteDelegate.EMAIL_SUBJECT_KEY, properties); - } /** * Sends an invitation email. @@ -140,128 +88,27 @@ public class InviteSender * @param emailSubjectKey the subject of the email * @param properties A Map containing the properties needed to send the email. */ - public void sendMail(String emailTemplateXpath, String emailSubjectKey, Map properties) - { - checkProperties(properties); - ParameterCheck.mandatory("Properties", properties); - NodeRef inviter = personService.getPerson(properties.get(wfVarInviterUserName)); - String inviteeName = properties.get(wfVarInviteeUserName); - NodeRef invitee = personService.getPerson(inviteeName); - Action mail = actionService.createAction(MailActionExecuter.NAME); - mail.setParameterValue(MailActionExecuter.PARAM_FROM, getEmail(inviter)); - mail.setParameterValue(MailActionExecuter.PARAM_TO, getEmail(invitee)); - mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, emailSubjectKey); - mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT_PARAMS, new Object[] {ModelUtil.getProductName(repoAdminService), getSiteName(properties)}); - mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, getEmailTemplateNodeRef(emailTemplateXpath)); - mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, - (Serializable)buildMailTextModel(properties, inviter, invitee)); - mail.setParameterValue(MailActionExecuter.PARAM_IGNORE_SEND_FAILURE, true); - actionService.executeAction(mail, getWorkflowPackage(properties)); - } + public abstract void sendMail(String emailTemplateXpath, String emailSubjectKey, Map properties); + + protected abstract Map buildMailTextModel(Map properties); + + protected abstract List getRequiredProperties(); /** * @param properties Map */ - private void checkProperties(Map properties) + protected void checkProperties(Map properties) { Set keys = properties.keySet(); - if (!keys.containsAll(expectedProperties)) + if (!keys.containsAll(getRequiredProperties())) { - LinkedList missingProperties = new LinkedList(expectedProperties); + LinkedList missingProperties = new LinkedList(getRequiredProperties()); missingProperties.removeAll(keys); throw new InvitationException("The following mandatory properties are missing:\n" + missingProperties); } } - private Map buildMailTextModel(Map properties, NodeRef inviter, NodeRef invitee) - { - // Set the core model parts - // Note - the user part is skipped, as that's implied via the run-as - Map model = new HashMap(); - model.put(TemplateService.KEY_COMPANY_HOME, repository.getCompanyHome()); - model.put(TemplateService.KEY_USER_HOME, repository.getUserHome(repository.getPerson())); - model.put(TemplateService.KEY_PRODUCT_NAME, ModelUtil.getProductName(repoAdminService)); - - // Build up the args for rendering inside the template - Map args = buildArgs(properties, inviter, invitee); - model.put("args", (Serializable)args); - - // All done - return model; - } - - private Map buildArgs(Map properties, NodeRef inviter, NodeRef invitee) - { - String params = buildUrlParamString(properties); - String acceptLink = makeLink(properties.get(wfVarServerPath), properties.get(wfVarAcceptUrl), params, null); - String rejectLink = makeLink(properties.get(wfVarServerPath), properties.get(wfVarRejectUrl), params, null); - - String siteDashboardEndpoint = getSiteDashboardEndpoint(properties); - String siteDashboardLink = makeLink(properties.get(wfVarServerPath), - siteDashboardEndpoint, null, null); - String siteLeaveLink = makeLink(properties.get(wfVarServerPath), - siteDashboardEndpoint, null, SITE_LEAVE_HASH); - - Map args = new HashMap(); - args.put("inviteePersonRef", invitee.toString()); - args.put("inviterPersonRef", inviter.toString()); - args.put("siteName", getSiteName(properties)); - args.put("inviteeSiteRole", getRoleName(properties)); - args.put("inviteeUserName", properties.get(wfVarInviteeUserName)); - args.put("inviteeGenPassword", properties.get(wfVarInviteeGenPassword)); - args.put("acceptLink", acceptLink); - args.put("rejectLink", rejectLink); - args.put("siteDashboardLink", siteDashboardLink); - args.put("siteLeaveLink", siteLeaveLink); - return args; - } - - protected String makeLink(String location, String endpoint, String queryParams, String hashParam) - { - location = location.endsWith("/") ? location : location + "/"; - endpoint = endpoint.startsWith("/") ? endpoint.substring(1) : endpoint; - if (queryParams != null) - { - queryParams = queryParams.startsWith("?") ? queryParams : "?" + queryParams; - } - else - { - queryParams = ""; - } - if (hashParam != null) - { - hashParam = hashParam.startsWith("#") ? hashParam : "#" + hashParam; - } - else - { - hashParam = ""; - } - return location + endpoint + queryParams + hashParam; - } - - private String getRoleName(Map properties) - { - String roleName = properties.get(wfVarRole); - String role = messageService.getMessage("invitation.invitesender.email.role." + roleName); - if (role == null) - { - role = roleName; - } - return role; - } - - private String getEmail(NodeRef person) - { - return (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); - } - - private NodeRef getWorkflowPackage(Map properties) - { - String packageRef = properties.get(WF_PACKAGE); - return new NodeRef(packageRef); - } - - private NodeRef getEmailTemplateNodeRef(String emailTemplateXPath) + protected NodeRef getEmailTemplateNodeRef(String emailTemplateXPath) { List nodeRefs = searchService.selectNodes(repository.getRootHome(), emailTemplateXPath, null, @@ -280,22 +127,9 @@ public class InviteSender } } - private String buildUrlParamString(Map properties) + protected String getSiteName(Map properties) { - StringBuilder params = new StringBuilder("?inviteId="); - params.append(properties.get(WF_INSTANCE_ID)); - params.append("&inviteeUserName="); - params.append(URLEncoder.encode(properties.get(wfVarInviteeUserName))); - params.append("&siteShortName="); - params.append(properties.get(wfVarResourceName)); - params.append("&inviteTicket="); - params.append(properties.get(wfVarInviteTicket)); - return params.toString(); - } - - private String getSiteName(Map properties) - { - String siteFullName = properties.get(wfVarResourceName); + String siteFullName = properties.get(getWorkflowPropForSiteName()); SiteInfo site = siteService.getSite(siteFullName); if (site == null) throw new InvitationException("The site " + siteFullName + " could not be found."); @@ -307,11 +141,8 @@ public class InviteSender siteName = siteTitle; } return siteName; - } + } + + protected abstract String getWorkflowPropForSiteName(); - private String getSiteDashboardEndpoint(Map properties) - { - String siteName = properties.get(wfVarResourceName); - return MessageFormat.format(SITE_DASHBOARD_ENDPOINT_PATTERN, siteName); - } } diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationService.java b/source/java/org/alfresco/service/cmr/invitation/InvitationService.java index aaec896ef6..29947ef1f5 100644 --- a/source/java/org/alfresco/service/cmr/invitation/InvitationService.java +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationService.java @@ -27,7 +27,7 @@ package org.alfresco.service.cmr.invitation; import java.util.List; import java.util.Map; - + import org.alfresco.service.Auditable; import org.alfresco.service.NotAuditable; @@ -324,6 +324,17 @@ public interface InvitationService * @return true if emails are sent on invite. */ @NotAuditable - boolean isSendEmails(); - + boolean isSendEmails(); + + /** + * + * Sends the site join request notification email using the given template, subject localization key, and variables. + * + * @param invitationId + * @param emailTemplateXpath + * @param emailSubjectKey + * @param variables + */ + @Auditable(parameters = { "inviteId" }) + public void sendModeratedInvitation(String invitationId, String emailTemplateXpath, String emailSubjectKey, Map variables); } diff --git a/source/test-java/org/alfresco/AllUnitTestsSuite.java b/source/test-java/org/alfresco/AllUnitTestsSuite.java index 07953ad775..cc7ddb9616 100644 --- a/source/test-java/org/alfresco/AllUnitTestsSuite.java +++ b/source/test-java/org/alfresco/AllUnitTestsSuite.java @@ -63,7 +63,8 @@ public class AllUnitTestsSuite extends TestSuite suite.addTestSuite(org.alfresco.repo.forms.processor.node.FieldProcessorTest.class); suite.addTestSuite(org.alfresco.repo.forms.processor.workflow.TaskFormProcessorTest.class); suite.addTestSuite(org.alfresco.repo.forms.processor.workflow.WorkflowFormProcessorTest.class); - suite.addTestSuite(org.alfresco.repo.invitation.site.InviteSenderTest.class); + suite.addTestSuite(org.alfresco.repo.invitation.site.InviteSenderTest.class); + suite.addTestSuite(org.alfresco.repo.invitation.site.InviteModeratedSenderTest.class); suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.lock.LockUtilsTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.lock.mem.LockStoreImplTest.class)); suite.addTestSuite(org.alfresco.repo.module.ModuleDetailsImplTest.class); diff --git a/source/test-java/org/alfresco/repo/invitation/site/InviteModeratedSenderTest.java b/source/test-java/org/alfresco/repo/invitation/site/InviteModeratedSenderTest.java new file mode 100644 index 0000000000..2b422ed5c1 --- /dev/null +++ b/source/test-java/org/alfresco/repo/invitation/site/InviteModeratedSenderTest.java @@ -0,0 +1,269 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.invitation.site; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import 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; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.admin.RepoAdminService; +import org.alfresco.service.cmr.admin.RepoUsage; +import org.alfresco.service.cmr.admin.RepoUsage.LicenseMode; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.security.PersonService.PersonInfo; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.mockito.ArgumentCaptor; + + + +/** + * @author Constantin Popa + */ +public class InviteModeratedSenderTest extends TestCase +{ + private static final StoreRef testStore = new StoreRef(StoreRef.PROTOCOL_TEST, "test"); + + private static final String requesterFirstName = "FirstName"; + private static final String requesterLastName = "LastName"; + private static final String requesterUserName = "JustAUserName"; + private static final String requesterRole = "SiteConsumer"; + private static final String requesterMail = "req@mail.alf"; + private static final NodeRef requesterNodeRef = new NodeRef(testStore, requesterUserName); + + private static final String SiteManagerGroup = "Group_site_manager"; + + private static final NodeRef emailTemplateNodeRef = new NodeRef(testStore, "emailTemplate"); + + private static final String fullSiteName = "Full Site Name"; + private static final String shortSiteName = "site-name"; + + private static final String packageId = testStore + "/Package"; + private static final String pendingInvitesLink = MessageFormat.format("page/site/{0}/pending-invites", shortSiteName); + + private final MessageService messageService = mock(MessageService.class); + + private Action mailAction; + private SiteInfo siteInfo = mock(SiteInfo.class); + private InviteModeratedSender inviteModeratedSender; + + @SuppressWarnings("rawtypes") + /** + * Test that the mail action is correctly constructed when sending notifications emails about users requesting access to a specific site + * @throws Exception + */ + public void testSendModeratedEmail() throws Exception + { + Map properties = buildDefaultProperties(); + inviteModeratedSender.sendMail(SendModeratedInviteDelegate.EMAIL_TEMPLATE_XPATH, SendModeratedInviteDelegate.EMAIL_SUBJECT_KEY, properties); + + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_FROM), eq(requesterMail)); + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_TO_MANY), eq(SiteManagerGroup)); + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_SUBJECT), eq(SendModeratedInviteDelegate.EMAIL_SUBJECT_KEY)); + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_SUBJECT_PARAMS), eq(new Object[]{fullSiteName})); + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_TEMPLATE), eq(emailTemplateNodeRef)); + + ArgumentCaptor modelC = ArgumentCaptor.forClass(Map.class); + verify(mailAction).setParameterValue(eq(MailActionExecuter.PARAM_TEMPLATE_MODEL), (Serializable)modelC.capture()); + + // Check the model + Map model = modelC.getValue(); + assertNotNull(model); + assertEquals(false, model.isEmpty()); + assertNotNull(model.get("productName")); + assertEquals(model.get("inviteeName"), requesterFirstName + " " + requesterLastName); + assertEquals(model.get("siteName"), fullSiteName); + assertEquals(model.get("sharePendingInvitesLink"), pendingInvitesLink); + + } + + private Map buildDefaultProperties() + { + Map properties = new HashMap(); + properties.put(WorkflowModelModeratedInvitation.wfVarInviteeUserName, requesterUserName); + properties.put(WorkflowModelModeratedInvitation.wfVarInviteeRole, requesterRole); + properties.put(WorkflowModelModeratedInvitation.wfVarResourceName, shortSiteName); + properties.put(WorkflowModelModeratedInvitation.bpmGroupAssignee, SiteManagerGroup); + properties.put(WorkflowModelModeratedInvitation.wfVarResourceType, "website"); + properties.put(InviteNominatedSender.WF_PACKAGE, packageId); + + return properties; + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + ServiceRegistry services = mockServices(); + Repository repository = mockRepository(); + inviteModeratedSender = new InviteModeratedSender(services, repository, messageService); + } + + /** + * Mocks up a Repository that will return the inviter as the current user. + * + * @return Repository + */ + private Repository mockRepository() + { + Repository repository = mock(Repository.class); + when(repository.getPerson()).thenReturn(requesterNodeRef); + return repository; + } + + /** + * @return ServiceRegistry + */ + private ServiceRegistry mockServices() + { + ActionService mockActionService = mockActionService(); + NodeService mockNodeService = mockNodeService(); + PersonService mockPersonService = mockPersonService(); + SearchService mockSearchService = mockSearchService(); + SiteService mockSiteService = mockSiteService(); + FileFolderService mockFileFolderService = mockFileFolderService(); + + ServiceRegistry services = mock(ServiceRegistry.class); + when(services.getActionService()).thenReturn(mockActionService); + when(services.getNodeService()).thenReturn(mockNodeService); + when(services.getPersonService()).thenReturn(mockPersonService); + when(services.getSearchService()).thenReturn(mockSearchService); + when(services.getSiteService()).thenReturn(mockSiteService); + when(services.getFileFolderService()).thenReturn(mockFileFolderService); + return services; + } + + private FileFolderService mockFileFolderService() + { + FileFolderService fileFolderService = mock(FileFolderService.class); + when(fileFolderService.getLocalizedSibling(emailTemplateNodeRef)).thenReturn(emailTemplateNodeRef); + return fileFolderService; + } + + /** + * Mocks up a SiteService that returns appropriate SiteInfo. + * + * @return SiteService + */ + private SiteService mockSiteService() + { + SiteService siteService = mock(SiteService.class); + when(siteInfo.getTitle()).thenReturn(fullSiteName); + when(siteService.getSite(shortSiteName)).thenReturn(siteInfo); + return siteService; + } + + /** + * Mocks up a SearchService that will return the template NodeRef when* queried. + * + * @return SearchService + */ + private SearchService mockSearchService() + { + SearchService searchService = mock(SearchService.class); + ResultSet results = mock(ResultSet.class); + List nodeRefs = Arrays.asList(emailTemplateNodeRef); + when(results.getNodeRefs()).thenReturn(nodeRefs); + when(searchService.query((SearchParameters) any())).thenReturn(results); + when(searchService.selectNodes(any(NodeRef.class), any(String.class), + any(QueryParameterDefinition[].class), any(NamespacePrefixResolver.class), eq(false))) + .thenReturn(nodeRefs); + return searchService; + } + + /** + * Mocks up a PersonService that returns the correct NodeRef when given a* user name. + * + * @return PersonService + */ + private PersonService mockPersonService() + { + PersonService personService = mock(PersonService.class); + + when(personService.getPerson(requesterUserName)).thenReturn(requesterNodeRef); + when(personService.getPerson(requesterNodeRef)).thenReturn(new PersonInfo(requesterNodeRef, requesterUserName, requesterFirstName, requesterLastName)); + return personService; + } + + /** + * Mocks up NodeService to return email adresses for requester + * + * @return NodeService + */ + private NodeService mockNodeService() + { + NodeService nodeService = mock(NodeService.class); + when(nodeService.getProperty(requesterNodeRef, ContentModel.PROP_EMAIL)).thenReturn(requesterMail); + return nodeService; + } + + /** + * Mocks up an ActionService which returns the mailAction field when + * createAction() is called. + * + * @return ActionService + */ + private ActionService mockActionService() + { + mailAction = mock(Action.class); + + ActionService actionService = mock(ActionService.class); + when(actionService.createAction(MailActionExecuter.NAME)).thenReturn(mailAction); + return actionService; + } +} diff --git a/source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java b/source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java index 27d3d006b5..07f619de54 100644 --- a/source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java +++ b/source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java @@ -115,7 +115,7 @@ public class InviteSenderTest extends TestCase private final MessageService messageService = mock(MessageService.class); private Action mailAction; private SiteInfo siteInfo = mock(SiteInfo.class); - private InviteSender sender; + private InviteNominatedSender sender; private Map lastSetMailModel; protected void testSendMailWorkingPath( @@ -355,8 +355,8 @@ public class InviteSenderTest extends TestCase properties.put(wfVarInviteeGenPassword, password); properties.put(wfVarInviteTicket, ticket); properties.put(wfVarServerPath, path); - properties.put(InviteSender.WF_PACKAGE, packageId); - properties.put(InviteSender.WF_INSTANCE_ID, instanceId); + properties.put(InviteNominatedSender.WF_PACKAGE, packageId); + properties.put(InviteNominatedSender.WF_INSTANCE_ID, instanceId); return properties; } @@ -366,7 +366,7 @@ public class InviteSenderTest extends TestCase super.setUp(); ServiceRegistry services = mockServices(); Repository repository = mockRepository(); - sender = new InviteSender(services, repository, messageService); + sender = new InviteNominatedSender(services, repository, messageService); lastSetMailModel = null; }