diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.desc.xml
index de4b24d846..f14e42088a 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.desc.xml
@@ -1,8 +1,9 @@
Invite
Processes Inviter actions ('start' or 'cancel' invite)
- /api/invite/{action}?site={siteShortName}
- extension
+ /api/invite/start?inviteeEmail={inviteeEmailAddress}&siteShortName={siteShortName}
+ /api/invite/cancel?workflowId={workflowId}
+
user
required
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.js b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.js
index 2f9b2130f4..3893115a8d 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.js
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.js
@@ -1,33 +1,57 @@
+var WORKFLOW_DEFINITION_NAME = "jbpm$wf:invite";
+var ACTION_START = "start";
+var ACTION_CANCEL = "cancel";
+var TRANSITION_SEND_INVITE = "sendInvite";
+
/**
* Starts the Invite workflow
*
* @method start
* @static
+ * @param inviteeEmail string email address of invitee
* @param siteShortName string short name of site that the invitee is being
* invited to by the inviter
*
*/
-function start(siteShortName)
+function start(inviteeEmail, siteShortName)
{
- var wfDefinition = workflow.getDefinitionByName("wf:invite");
+ var wfDefinition = workflow.getDefinitionByName(WORKFLOW_DEFINITION_NAME);
- // create invitee with generated user name and password, and with a
+ // handle workflow definition does not exist
+ if (wfDefinition === null)
+ {
+ status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Workflow definition "
+ + "for name " + WORKFLOW_DEFINITION_NAME + " does not exist");
+ return;
+ }
+
+ // create invitee person with generated user name and password, and with a
// disabled user account
var invitee = people.createPerson(true, false);
+ invitee.properties["cm:email"] = inviteeEmail;
+ invitee.save();
- // create workflow properties, containing inviter and invitee user name,
- // and site short name
+ var inviteeUserName = invitee.properties.userName;
+
+ // create workflow properties
var workflowProps = [];
- workflowProps["inviterUserName"] = person.properties.userName;
- workflowProps["inviteeUserName"] = invitee.properties.userName;
- workflowProps["siteShortName"] = siteShortName;
- wfDefinition.startWorkflow(null, workflowProps);
+ workflowProps["wf:inviterUserName"] = person.properties.userName;
+ workflowProps["wf:inviteeUserName"] = inviteeUserName;
+ workflowProps["wf:inviteeGenPassword"] = invitee.properties.generatedPassword;
+ workflowProps["wf:siteShortName"] = siteShortName;
- // add action context info to model
- model.action = "start";
+ // start the workflow
+ var wfPath = wfDefinition.startWorkflow(workflowProps);
+ var workflowId = wfPath.instance.id;
+
+ // send out the invite
+ wfPath.signal(TRANSITION_SEND_INVITE);
+
+ // add action info to model for template processing
+ model.action = ACTION_START;
+ model.workflowId = workflowId;
model.inviteeUserName = inviteeUserName;
model.siteShortName = siteShortName;
-
}
/**
@@ -42,10 +66,20 @@ function start(siteShortName)
function cancel(workflowId)
{
var workflowInstance = workflow.getInstance(workflowId);
+
+ // handle workflow instance for given workflow ID does not exist
+ if (workflowInstance === null)
+ {
+ status.setCode(status.STATUS_BAD_REQUEST, "Workflow instance for given "
+ + "workflow ID " + workflowId + " does not exist");
+ return;
+ }
+
+ // cancel the workflow
workflowInstance.cancel();
- // add action context info to model
- model.action = "cancel";
+ // add action info to model for template
+ model.action = ACTION_CANCEL;
model.workflowId = workflowId;
}
@@ -57,60 +91,77 @@ function cancel(workflowId)
*/
function main()
{
- // check that the action ('start' or 'cancel') has been provided on the URL
- // and that URL parameters have been provided
- if ((url.extension === null) || (args.length == 0))
+ // extract action string from URL
+ var action = null;
+ var actionStartIndex = url.service.lastIndexOf("/") + 1;
+ if (actionStartIndex <= url.service.length() - 1)
{
- // handle action not provided or no parameters given
- status.code = 400;
- status.message = "Action has not been provided in URL or " +
- "no parameters have been provided on URL";
- status.redirect = true;
+ action = url.service.substring(actionStartIndex, url.service.length());
}
+
+ // check that the action has been provided on the URL
+ // and that URL parameters have been provided
+ if ((action === null) || (action.length == 0))
+ {
+ // handle action not provided on URL
+ status.setCode(status.STATUS_BAD_REQUEST, "Action has not been provided in URL");
+ return;
+ }
+
+ // handle no parameters given on URL
+ if (args.length == 0)
+ {
+ status.setCode(status.STATUS_BAD_REQUEST, "No parameters have been provided on URL");
+ return;
+ }
+
+ // handle action 'start'
+ if (action == ACTION_START)
+ {
+ // check for 'inviteeEmail' parameter not provided
+ if ((args["inviteeEmail"] === null) || (args["inviteeEmail"].length == 0))
+ {
+ // handle inviteeEmail URL parameter not provided
+ status.setCode(status.STATUS_BAD_REQUEST, "'inviteeEmail' parameter " +
+ "has not been provided in URL for action '" + ACTION_START + "'");
+ return;
+ }
+
+ // check for 'siteShortName' parameter not provided
+ if ((args["siteShortName"] === null) || (args["siteShortName"].length == 0))
+ {
+ // handle siteShortName URL parameter not provided
+ status.setCode(status.STATUS_BAD_REQUEST, "'siteShortName' parameter " +
+ "has not been provided in URL for action '" + ACTION_START + "'");
+ return;
+ }
+
+ // process action 'start' with provided parameters
+ var inviteeEmail = args["inviteeEmail"];
+ var siteShortName = args["siteShortName"];
+ start(inviteeEmail, siteShortName);
+ }
+ // else handle if provided 'action' is 'cancel'
+ else if (action == ACTION_CANCEL)
+ {
+ // check for 'workflowId' parameter not provided
+ if ((args["workflowId"] === null) || (args["workflowId"].length == 0))
+ {
+ // handle workflowId URL parameter not provided
+ status.setCode(status.STATUS_BAD_REQUEST, "'workflowId' parameter has "
+ + "not been provided in URL for action '" + ACTION_CANCEL +"'");
+ return;
+ }
+
+ // process action 'cancel' with provided parameters
+ var workflowId = args["workflowId"];
+ cancel(workflowId);
+ }
+ // handle action not recognised
else
{
- // handle action provided in URL ('start' or 'cancel')
- var action = url.extension;
-
- // handle if provided 'action' is 'start'
- if (action == "start")
- {
- // check for 'siteShortName' parameter
- if ((args["siteShortName"] === null) || (args["siteShortName"].length == 0))
- {
- // handle siteShortName URL parameter not provided
- status.code = 400;
- status.message = "'siteShortName' parameter has not been provided in URL for action 'start'";
- status.redirect = true;
- }
- else
- {
- start(args["siteShortName"]);
- }
- }
- // else handle if provided 'action' is 'cancel'
- else if (action == "cancel")
- {
- // check for 'workflowId' parameter
- if ((args["workflowId"] === null) || (args["workflowId"].length == 0))
- {
- // handle workflowId URL parameter not provided
- status.code = 400;
- status.message = "'workflowId' parameter has not been provided in URL for action 'cancel'";
- status.redirect = true;
- }
- else
- {
- cancel(args["workflowId"]);
- }
- }
- // handle action not recognised
- else
- {
- status.code = 400;
- status.message = "Action, '" + action + "', provided in URL has not been recognised.";
- status.redirect = true;
- }
+ status.setCode(status.STATUS_BAD_REQUEST, "Action, '" + action + "', "
+ + "provided in URL has not been recognised.");
}
}
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.json.ftl
new file mode 100644
index 0000000000..890cd90664
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/invite.get.json.ftl
@@ -0,0 +1,18 @@
+{
+ "action" : "${action}",
+ <#if workflowId??>
+ "workflowId" : "${workflowId}",
+ <#else>
+ "workflowId" : undefined,
+ #if>
+ <#if inviteeUserName??>
+ "inviteeUserName" : "${inviteeUserName}",
+ <#else>
+ "inviteeUserName" : undefined,
+ #if>
+ <#if siteShortName??>
+ "siteShortName" : "${siteShortName}"
+ <#else>
+ "siteShortName" : undefined
+ #if>
+}
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.desc.xml
index 9c4e619bf0..9539128faf 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.desc.xml
@@ -2,7 +2,7 @@
Invite Response
Processes invite response from Invitee
/api/inviteresponse/{response}?workflowId={workflowId}&inviteeUserName={inviteeUserName}&siteShortName={siteShortName}
- extension
+
guest
required
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.js b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.js
index ddb1b3b295..f392275c85 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.js
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.js
@@ -1,4 +1,6 @@
-var SITE_ROLE_COLLABORATOR = "collaborator";
+var SITE_ROLE_COLLABORATOR = "SiteCollaborator";
+var TRANSITION_ACCEPT = "accept";
+var TRANSITION_REJECT = "reject";
/**
* Processes 'accept' response from invitee
@@ -12,8 +14,22 @@ var SITE_ROLE_COLLABORATOR = "collaborator";
function accept(workflowId, inviteeUserName, siteShortName)
{
var wfInstance = workflow.getInstance(workflowId);
- var wfPath = wfInstance.getPaths()[0];
- wfPath.signal("accept");
+ var wfPaths = wfInstance.getPaths();
+
+ // return error message if no workflow paths found for
+ // supplied workflow id
+ if ((wfPaths === null) || (wfPaths.length == 0))
+ {
+ status.setCode(status.STATUS_INTERNAL_SERVER_ERROR,
+ "No workflow paths associated with workflow ID: "
+ + workflowId);
+ return;
+ }
+
+ // get the first workflow path off the workflow paths array
+ // (there should only be one) and signal a transition to "accept"
+ var wfPath = wfPaths[0];
+ wfPath.signal(TRANSITION_ACCEPT);
people.enablePerson(inviteeUserName);
@@ -21,8 +37,8 @@ function accept(workflowId, inviteeUserName, siteShortName)
* Find out role string that Invitee should be added to Site as
*/
// Add Invitee to Site
- var site = sites.getSite(siteShortName);
- site.setMembership(username, SITE_ROLE_COLLABORATOR);
+ var site = siteService.getSite(siteShortName);
+ site.setMembership(inviteeUserName, SITE_ROLE_COLLABORATOR);
// add data to appear in rendition
model.response = "accept";
@@ -40,9 +56,12 @@ function accept(workflowId, inviteeUserName, siteShortName)
*/
function reject(workflowId, inviteeUserName, siteShortName)
{
- var wfInstance = workflow.getInstance(wfid);
+ var wfInstance = workflow.getInstance(workflowId);
var wfPath = wfInstance.getPaths()[0];
- wfPath.signal("reject");
+ wfPath.signal(TRANSITION_REJECT);
+
+ // delete the person created for invitee
+ people.deletePerson(inviteeUserName);
// add data to appear in rendition
model.response = "reject";
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.json.ftl
new file mode 100644
index 0000000000..c0adf83a3a
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/invite/inviteresponse.get.json.ftl
@@ -0,0 +1,4 @@
+{
+ "response" : "${response}",
+ "siteShortName" : "${siteShortName}"
+}
diff --git a/config/alfresco/workflow/invite-workflow-model.xml b/config/alfresco/workflow/invite-workflow-model.xml
index b8b58b618d..4f53446d43 100644
--- a/config/alfresco/workflow/invite-workflow-model.xml
+++ b/config/alfresco/workflow/invite-workflow-model.xml
@@ -1,11 +1,12 @@
-
+
@@ -13,6 +14,24 @@
bpm:startTask
+
+
+ d:text
+
+
+ d:text
+
+
+ d:text
+
+
+ d:text
+
+
+
+
+
+ bpm:workflowTask
diff --git a/config/alfresco/workflow/invite_processdefinition.xml b/config/alfresco/workflow/invite_processdefinition.xml
index 5478137027..7a24201123 100644
--- a/config/alfresco/workflow/invite_processdefinition.xml
+++ b/config/alfresco/workflow/invite_processdefinition.xml
@@ -2,76 +2,80 @@
-
+
+
+
-
+
+
-
-
+
+
-
+
+
-
-
+
-
-
+
+
-
-
-
-
-
+
-
+
diff --git a/source/java/org/alfresco/repo/web/scripts/invite/InviteServiceTest.java b/source/java/org/alfresco/repo/web/scripts/invite/InviteServiceTest.java
new file mode 100644
index 0000000000..785158edb7
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/invite/InviteServiceTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2005-2007 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have received a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.web.scripts.invite;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.site.SiteInfo;
+import org.alfresco.repo.site.SiteService;
+import org.alfresco.repo.web.scripts.BaseWebScriptTest;
+import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.util.PropertyMap;
+import org.alfresco.web.scripts.Status;
+import org.json.JSONObject;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+/**
+ * Unit Test to test Invite Web Script API
+ *
+ * @author Glen Johnson at Alfresco dot com
+ */
+public class InviteServiceTest extends BaseWebScriptTest
+{
+ private AuthenticationService authenticationService;
+ private AuthenticationComponent authenticationComponent;
+ private PersonService personService;
+ private SiteService siteService;
+
+ private static final String USER_ADMIN = "admin";
+ private static final String USER_INVITER = "InviteeUser";
+ private static final String INVITEE_EMAIL = "inviter123@email.com";
+ private static final String SITE_SHORT_NAME_INVITE = "InviteSiteShortName";
+
+ private static final String URL_INVITE_SERVICE = "/api/invite";
+ private static final String URL_INVITERSP_SERVICE = "/api/inviteresponse";
+
+ private static final String INVITE_ACTION_START = "start";
+ private static final String INVITE_ACTION_CANCEL = "cancel";
+
+ private static final String INVITE_RSP_ACCEPT = "accept";
+ private static final String INVITE_RSP_REJECT = "reject";
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ this.authenticationService = (AuthenticationService)getServer().getApplicationContext().getBean("AuthenticationService");
+ this.authenticationComponent = (AuthenticationComponent)getServer().getApplicationContext().getBean("authenticationComponent");
+ this.personService = (PersonService)getServer().getApplicationContext().getBean("PersonService");
+ this.siteService = (SiteService)getServer().getApplicationContext().getBean("siteService");
+
+ // Create inviter user
+ createUser(USER_INVITER);
+
+ // Create site for Inviter to invite Invitee to
+ // - only create the site if it doesn't already exist
+ SiteInfo siteInfo = this.siteService.getSite(SITE_SHORT_NAME_INVITE);
+ if (siteInfo == null)
+ {
+ this.siteService.createSite("InviteSitePreset", SITE_SHORT_NAME_INVITE, "InviteSiteTitle",
+ "InviteSiteDescription", true);
+ }
+
+ // Do tests as inviter user
+ this.authenticationComponent.setCurrentUser(USER_INVITER);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ // admin user required to delete user
+ this.authenticationComponent.setCurrentUser(USER_ADMIN);
+
+ // delete the inviter user
+ personService.deletePerson(USER_INVITER);
+
+ // delete invite site
+ siteService.deleteSite(SITE_SHORT_NAME_INVITE);
+ }
+
+ private void createUser(String userName)
+ {
+ // if user with given user name doesn't already exist then create user
+ if (this.authenticationService.authenticationExists(userName) == false)
+ {
+ // create user
+ this.authenticationService.createAuthentication(userName, "password".toCharArray());
+
+ // create person properties
+ PropertyMap personProps = new PropertyMap();
+ personProps.put(ContentModel.PROP_USERNAME, userName);
+ personProps.put(ContentModel.PROP_FIRSTNAME, "FirstName123");
+ personProps.put(ContentModel.PROP_LASTNAME, "LastName123");
+ personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com");
+ personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123");
+ personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123");
+
+ // create person node for user
+ this.personService.createPerson(personProps);
+ }
+ }
+
+ private JSONObject startInvite(String inviteeEmail, String siteShortName, int expectedStatus)
+ throws Exception
+ {
+ // Inviter sends invitation to Invitee to join a Site
+ String startInviteUrl = URL_INVITE_SERVICE + "/" + INVITE_ACTION_START + "?inviteeEmail=" + inviteeEmail
+ + "&siteShortName=" + siteShortName;
+ MockHttpServletResponse response = getRequest(startInviteUrl, expectedStatus);
+
+ JSONObject result = new JSONObject(response.getContentAsString());
+
+ return result;
+ }
+
+ public void testStartInvite() throws Exception
+ {
+ // admin user required to delete user
+ this.authenticationComponent.setCurrentUser(USER_ADMIN);
+
+ JSONObject result = startInvite(INVITEE_EMAIL, SITE_SHORT_NAME_INVITE, Status.STATUS_OK);
+
+ assertEquals(INVITE_ACTION_START, result.get("action"));
+ assertEquals(SITE_SHORT_NAME_INVITE, result.get("siteShortName"));
+ }
+
+ public void testCancelInvite() throws Exception
+ {
+ // admin user required to delete user
+ this.authenticationComponent.setCurrentUser(USER_ADMIN);
+
+ // inviter starts invite workflow
+ JSONObject result = startInvite(INVITEE_EMAIL, SITE_SHORT_NAME_INVITE, Status.STATUS_OK);
+
+ // get hold of workflow ID of started invite workflow instance
+ String workflowId = result.getString("workflowId");
+
+ // Inviter cancels pending invitation
+ String cancelInviteUrl = URL_INVITE_SERVICE + "/" + INVITE_ACTION_CANCEL + "?workflowId=" + workflowId;
+ MockHttpServletResponse response = getRequest(cancelInviteUrl, Status.STATUS_OK);
+ }
+
+ public void testAcceptInvite() throws Exception
+ {
+ // admin user required to delete user
+ this.authenticationComponent.setCurrentUser(USER_ADMIN);
+
+ // inviter starts invite workflow
+ JSONObject result = startInvite(INVITEE_EMAIL, SITE_SHORT_NAME_INVITE, Status.STATUS_OK);
+
+ // get hold of workflow ID of started invite workflow instance
+ String workflowId = result.getString("workflowId");
+
+ // get hold of invitee user name that was generated as part of starting the invite
+ String inviteeUserName = result.getString("inviteeUserName");
+
+ // Invitee accepts invitation to a Site from Inviter
+ String acceptInviteUrl = URL_INVITERSP_SERVICE + "/" + INVITE_RSP_ACCEPT + "?workflowId=" + workflowId
+ + "&inviteeUserName=" + inviteeUserName + "&siteShortName=" + SITE_SHORT_NAME_INVITE;
+ MockHttpServletResponse response = getRequest(acceptInviteUrl, Status.STATUS_OK);
+ }
+
+ public void testRejectInvite() throws Exception
+ {
+ // admin user required to delete user
+ this.authenticationComponent.setCurrentUser(USER_ADMIN);
+
+ // inviter starts invite workflow
+ JSONObject result = startInvite(INVITEE_EMAIL, SITE_SHORT_NAME_INVITE, Status.STATUS_OK);
+
+ // get hold of workflow ID of started invite workflow instance
+ String workflowId = result.getString("workflowId");
+
+ // get hold of invitee user name that was generated as part of starting the invite
+ String inviteeUserName = result.getString("inviteeUserName");
+
+ // Invitee rejects invitation to a Site from Inviter
+ String rejectInviteUrl = URL_INVITERSP_SERVICE + "/" + INVITE_RSP_REJECT + "?workflowId=" + workflowId
+ + "&inviteeUserName=" + inviteeUserName + "&siteShortName=" + SITE_SHORT_NAME_INVITE;
+ MockHttpServletResponse response = getRequest(rejectInviteUrl, Status.STATUS_OK);
+ }
+}
\ No newline at end of file