diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 4c104d7706..c4a54b0294 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -38,6 +38,7 @@ + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index aed09ecd20..5f824881bd 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -371,18 +371,38 @@ text/xml false + + + jbpm + alfresco/workflow/invitation-nominated_processdefinition.xml + text/xml + false + + + + jbpm + alfresco/workflow/invitation-moderated_processdefinition.xml + text/xml + true + + + alfresco/workflow/workflowModel.xml alfresco/workflow/wcmWorkflowModel.xml + alfresco/workflow/invitation-nominated-workflow-model.xml + alfresco/workflow/invitation-moderated-workflow-model.xml alfresco/workflow/workflow-messages alfresco/workflow/wcm-workflow-messages + alfresco/workflow/invitation-nominated-workflow-messages + alfresco/workflow/invitation-moderated-workflow-messages diff --git a/config/alfresco/bootstrap/remote-api-context.xml b/config/alfresco/bootstrap/remote-api-context.xml new file mode 100644 index 0000000000..3b322f2787 --- /dev/null +++ b/config/alfresco/bootstrap/remote-api-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + jbpm + alfresco/workflow/invite_processdefinition.xml + text/xml + false + + + + + + + alfresco/workflow/invite-workflow-model.xml + + + + + + alfresco/workflow/invite-workflow-messages + + + + \ No newline at end of file diff --git a/config/alfresco/invitation-service-context.xml b/config/alfresco/invitation-service-context.xml new file mode 100644 index 0000000000..ce5e8d454c --- /dev/null +++ b/config/alfresco/invitation-service-context.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + org.alfresco.service.cmr.invitation.InvitationService + + + + invitationService + + + + + + + + + + + + + + + alfresco.messages.invitation-service + + + + + \ No newline at end of file diff --git a/config/alfresco/messages/invitation-service.properties b/config/alfresco/messages/invitation-service.properties new file mode 100644 index 0000000000..3c2dd84a7f --- /dev/null +++ b/config/alfresco/messages/invitation-service.properties @@ -0,0 +1,7 @@ +# Invitation service + +invitation.error.not_found "Invitation not found invitationId: {0}" + +invitation.cancel.not_site_manager "Current user, {0}, cannot cancel invitation: {1} because they are not a Site Manager for site: {2} + +invitation.invite.unable_generate_id "Unable to generate a user name for invitee, which doesn't already belong to someone else firstName:{0} lastName:{1} email:{2}" \ No newline at end of file diff --git a/config/alfresco/messages/site-service.properties b/config/alfresco/messages/site-service.properties new file mode 100644 index 0000000000..18e64db3d4 --- /dev/null +++ b/config/alfresco/messages/site-service.properties @@ -0,0 +1,11 @@ +# Site service externalised display strings + +site_service.unable_to_create=Unable to create site because the site short name {0} is already in use. Site short names must be unique. +site_service.can_not_update=Can not update site {0} because it does not exist. +site_service.can_not_delete=Can not delete site {0} because it does not exist. +site_service.site_no_exist=Site {0} does not exist. +site_service.do_not_remove_manager=A site requires at least one site manager. You can not remove {0} from the site membership because they are currently the only site manager. +site_service.can_not_reomve_memebership=The current user does not have sufficient permissions to delete membership details of the site {0}. +site_service.do_not_change_manager=A site requires at least one site manager. You can not change the role of {0}, because they are currently the only site manager. +site_service.can_not_change_memebership=The current user does not have permissions to modify the membership details of the site {0}. +site_service.site_container_not_folder=Site container {0} does not refer to a folder. \ No newline at end of file diff --git a/config/alfresco/model/siteModel.xml b/config/alfresco/model/siteModel.xml index c83aadb6c9..a96a7d51d9 100644 --- a/config/alfresco/model/siteModel.xml +++ b/config/alfresco/model/siteModel.xml @@ -40,6 +40,10 @@ Site Preset d:text + + Site Visibility + d:text + cm:titled @@ -47,7 +51,7 @@ - + Sites cm:folder diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml index 67325f0f11..0acaff9506 100644 --- a/config/alfresco/site-services-context.xml +++ b/config/alfresco/site-services-context.xml @@ -19,7 +19,7 @@ - org.alfresco.repo.site.SiteService + org.alfresco.service.cmr.site.SiteService @@ -50,6 +50,15 @@ + + + + + alfresco.messages.site-service + + + + diff --git a/config/alfresco/workflow/invitation-moderated-workflow-messages.properties b/config/alfresco/workflow/invitation-moderated-workflow-messages.properties new file mode 100644 index 0000000000..6e99ac6494 --- /dev/null +++ b/config/alfresco/workflow/invitation-moderated-workflow-messages.properties @@ -0,0 +1,12 @@ +# Display labels for out-of-the-box Site-oriented Workflows + +# +# Moderated Invitation +# + +wf_invitation-moderated.workflow.title=Invitation (Moderated) +wf_invitation-moderated.workflow.description=Moderated invitation to a resource such as a web site. + +wf_invitation-moderated-model.type.approveInvitationTask.title=Approve Invitation +wf_invitation-moderated-model.type.approveInvitationTask.description=Approve Invitation + diff --git a/config/alfresco/workflow/invitation-moderated-workflow-model.xml b/config/alfresco/workflow/invitation-moderated-workflow-model.xml new file mode 100644 index 0000000000..4160a5d16a --- /dev/null +++ b/config/alfresco/workflow/invitation-moderated-workflow-model.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + bpm:startTask + + bpm:assignee + bpm:groupAssignee + wf:moderatedInvitationStats + + + + + bpm:workflowTask + + + d:text + + + d:text + + + + + bpm:groupAssignee + cm:ownable + wf:moderatedInvitationStats + + + + + + + + + + + d:text + + + d:text + WEB_SITE + + + d:text + + + d:text + + + d:text + + + + + + + diff --git a/config/alfresco/workflow/invitation-moderated_processdefinition.xml b/config/alfresco/workflow/invitation-moderated_processdefinition.xml new file mode 100644 index 0000000000..d1dce8b99d --- /dev/null +++ b/config/alfresco/workflow/invitation-moderated_processdefinition.xml @@ -0,0 +1,60 @@ + + + + + + + + + #{bpm_assignee.properties['cm:userName']} + + + + + + + + + + + #{bpm_groupAssignee} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/workflow/invitation-nominated-workflow-messages.properties b/config/alfresco/workflow/invitation-nominated-workflow-messages.properties new file mode 100644 index 0000000000..ff7d322552 --- /dev/null +++ b/config/alfresco/workflow/invitation-nominated-workflow-messages.properties @@ -0,0 +1,40 @@ +# Display labels for out-of-the-box Site-oriented Workflows + +# +# Invite Workflow +# + +wf_invite.workflow.title=Invitation (Nominated) +wf_invite.workflow.description=Invitation to a Share Site, nominated by a site manager + +# Invite Task Definitions + +wf_invite-workflow-model.type.wf_inviteToSiteTask.title=Start Invite +wf_invite-workflow-model.type.wf_inviteToSiteTask.description=Start an invite to a Site +wf_invite-workflow-model.type.wf_invitePendingTask.title=Site Invite +wf_invite-workflow-model.type.wf_invitePendingTask.description=Invite to a Site +wf_invite-workflow-model.type.wf_rejectInviteTask.title=Rejected +wf_invite-workflow-model.type.wf_rejectInviteTask.description=Rejected +wf_invite-workflow-model.type.wf_acceptInviteTask.title=Accepted +wf_invite-workflow-model.type.wf_acceptInviteTask.description=Accepted + +# Invite Process Definitions + +wf_invite.node.start.title=Start +wf_invite.node.start.description=Start +wf_invite.node.invitePending.title=Invite Pending +wf_invite.node.invitePending.description=Invite Pending +wf_invite.node.invitePending.transition.reject.title=Reject +wf_invite.node.invitePending.transition.reject.description=Reject +wf_invite.node.invitePending.transition.accept.title=Accept +wf_invite.node.invitePending.transition.accept.description=Accept +wf_invite.node.inviteRejected.title=Rejected +wf_invite.node.inviteRejected.description=Rejected +wf_invite.task.wf_rejectInviteTask.title=Rejected +wf_invite.task.wf_rejectInviteTask.description=Rejected +wf_invite.node.inviteAccepted.title=Accepted +wf_invite.node.inviteAccepted.description=Accepted +wf_invite.task.wf_acceptInviteTask.title=Accepted +wf_invite.task.wf_acceptInviteTask.description=Accepted +wf_invite.node.end.title=End +wf_invite.node.end.description=End diff --git a/config/alfresco/workflow/invitation-nominated-workflow-model.xml b/config/alfresco/workflow/invitation-nominated-workflow-model.xml new file mode 100644 index 0000000000..538924c699 --- /dev/null +++ b/config/alfresco/workflow/invitation-nominated-workflow-model.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + bpm:startTask + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + d:text + + + + bpm:assignee + + + + + bpm:workflowTask + + + + bpm:workflowTask + + + + bpm:workflowTask + + + + diff --git a/config/alfresco/workflow/invitation-nominated_processdefinition.xml b/config/alfresco/workflow/invitation-nominated_processdefinition.xml new file mode 100644 index 0000000000..83541462a5 --- /dev/null +++ b/config/alfresco/workflow/invitation-nominated_processdefinition.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + #{bpm_assignee.properties['cm:userName']} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/admin/patch/impl/SitePermissionRefactorPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/SitePermissionRefactorPatch.java index 452d4657bc..63a233e895 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/SitePermissionRefactorPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/SitePermissionRefactorPatch.java @@ -30,14 +30,14 @@ import java.util.Set; import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.site.SiteInfo; import org.alfresco.repo.site.SiteModel; -import org.alfresco.repo.site.SiteService; import org.alfresco.repo.site.SiteServiceImpl; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; /** * Patch's the site permission model to use groups to contain users. diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java b/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java index 144eed0329..8c5e155176 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java @@ -88,11 +88,6 @@ public class AVMServiceTestBase extends TestCase protected static AuthenticationService fAuthService; - public void testSetup() - { - // NOOP - } - /** * Setup for AVM tests. Note that we set the polling * interval for the reaper to 4 seconds so that tests will diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index cd026556f0..570b059389 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -258,7 +258,9 @@ public class AVMStoreImpl implements AVMStore, Serializable } // Clear out the new nodes. List allLayeredNodeIDs = AVMDAOs.Instance().fAVMNodeDAO.getNewLayeredInStoreIDs(me); + AVMDAOs.Instance().fAVMNodeDAO.clearNewInStore(me); + AVMDAOs.Instance().fAVMNodeDAO.clear(); List layeredNodeIDs = new ArrayList(); for (Long layeredID : allLayeredNodeIDs) diff --git a/source/java/org/alfresco/repo/invitation/InvitationImpl.java b/source/java/org/alfresco/repo/invitation/InvitationImpl.java new file mode 100644 index 0000000000..bc151d4d66 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationImpl.java @@ -0,0 +1,75 @@ +package org.alfresco.repo.invitation; + +import org.alfresco.service.cmr.invitation.Invitation; +import org.alfresco.service.cmr.invitation.Invitation.ResourceType; + +/* package scope */ abstract class InvitationImpl +{ + /** + * Who is this invitation for + */ + private String inviteeUserName; + + /** + * Unique reference for this invitation + */ + private String inviteId; + + /** + * Which resource is this invitation for ? + */ + private String resourceName; + + /** + * What sort of invitation is this invitation for e.g. WEB_SITE or WEB_PROJECT + */ + private Invitation.ResourceType resourceType; + + + /** + * Create a new InvitationImpl + */ + public InvitationImpl() + { + super(); + } + + /** + * What sort of resource is it + * @return the resource type + */ + public ResourceType getResourceType() + { + return resourceType; + } + + public void setResourceType(ResourceType resourceType) + { + this.resourceType = resourceType; + } + + public void setInviteeUserName(String inviteeUserName) { + this.inviteeUserName = inviteeUserName; + } + + public String getInviteeUserName() { + return inviteeUserName; + } + + public void setInviteId(String inviteId) { + this.inviteId = inviteId; + } + + public String getInviteId() { + return inviteId; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getResourceName() { + return resourceName; + } + +} diff --git a/source/java/org/alfresco/repo/invitation/InvitationProcess.java b/source/java/org/alfresco/repo/invitation/InvitationProcess.java new file mode 100644 index 0000000000..5668bccf09 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationProcess.java @@ -0,0 +1,49 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.service.cmr.invitation.Invitation; + +/** + * The Invitation process is the interface provided by the invitation service to be + * implemented by each resource's invitation handler + * + * This invitation process is the unmoderated invite someone else. + */ + +public interface InvitationProcess +{ + /* + * someone starts the invitation process + */ + public Invitation invite(Invitation request, String comment); + + + /* + * cancel this request + */ + public void cancel (Invitation request); + +} diff --git a/source/java/org/alfresco/repo/invitation/InvitationProcessDescription.java b/source/java/org/alfresco/repo/invitation/InvitationProcessDescription.java new file mode 100644 index 0000000000..29d3b7bc0c --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationProcessDescription.java @@ -0,0 +1,30 @@ +/* + * 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 recieved 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.invitation; + +public interface InvitationProcessDescription +{ + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/invitation/InvitationProcessDescriptionImpl.java b/source/java/org/alfresco/repo/invitation/InvitationProcessDescriptionImpl.java new file mode 100644 index 0000000000..a2ce40f719 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationProcessDescriptionImpl.java @@ -0,0 +1,34 @@ +/* + * 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 recieved 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.invitation; + +/** + * Description about which invitation processes are available + */ +public class InvitationProcessDescriptionImpl implements InvitationProcessDescription +{ + +} diff --git a/source/java/org/alfresco/repo/invitation/InvitationSearchCriteriaImpl.java b/source/java/org/alfresco/repo/invitation/InvitationSearchCriteriaImpl.java new file mode 100644 index 0000000000..584eafff1b --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationSearchCriteriaImpl.java @@ -0,0 +1,46 @@ +package org.alfresco.repo.invitation; + +import org.alfresco.service.cmr.invitation.InvitationSearchCriteria; +import org.alfresco.service.cmr.invitation.Invitation.ResourceType; + +public class InvitationSearchCriteriaImpl implements InvitationSearchCriteria +{ + private String invitee; + private String inviter; + private String resourceName; + private ResourceType resourceType; + private InvitationSearchCriteria.InvitationType invitationType = InvitationSearchCriteria.InvitationType.ALL; + + public void setInvitee(String invitee) { + this.invitee = invitee; + } + public String getInvitee() { + return invitee; + } + public void setInviter(String inviter) { + this.inviter = inviter; + } + public String getInviter() { + return inviter; + } + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + public String getResourceName() { + return resourceName; + } + public void setResourceType(ResourceType resourceType) { + this.resourceType = resourceType; + } + public ResourceType getResourceType() { + return resourceType; + } + public InvitationType getInvitationType() + { + return invitationType; + } + public void setInvitationType(InvitationType invitationType) + { + this.invitationType = invitationType; + } +} diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java new file mode 100644 index 0000000000..703f5abd6c --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java @@ -0,0 +1,1198 @@ +/* + * 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 recieved 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.invitation; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.invitation.Invitation; +import org.alfresco.service.cmr.invitation.InvitationExceptionForbidden; +import org.alfresco.service.cmr.invitation.InvitationExceptionNotFound; +import org.alfresco.service.cmr.invitation.InvitationExceptionUserError; +import org.alfresco.service.cmr.invitation.InvitationSearchCriteria; +import org.alfresco.service.cmr.invitation.ModeratedInvitation; +import org.alfresco.service.cmr.invitation.NominatedInvitation; +import org.alfresco.service.cmr.invitation.InvitationService; +import org.alfresco.service.cmr.invitation.InvitationException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowInstance; +import org.alfresco.service.cmr.workflow.WorkflowPath; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskQuery; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.invitation.site.*; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.security.authentication.PasswordGenerator; +import org.alfresco.repo.security.authentication.UserNameGenerator; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.workflow.WorkflowModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation of invitation service. + * + * @see org.alfresco.service.cmr.invitation.Invitation + * + * @author mrogers + * + */ +public class InvitationServiceImpl implements InvitationService +{ + private static final Log logger = LogFactory + .getLog(InvitationServiceImpl.class); + + // maximum number of tries to generate a invitee user name which + // does not already belong to an existing person + public static final int MAX_NUM_INVITEE_USER_NAME_GEN_TRIES = 10; + + /** + * Start the invitation process for a NominatedInvitation + * + * @param inviteeFirstName + * @param inviteeLastName + * @param inviteeEmail + * @param inviteeUserName + * optional Alfresco user name of the invitee, null if not on + * system. + * @param Invitation + * .ResourceType resourceType + * @param resourceName + * @param inviteeRole + * @param serverPath + * @param acceptUrl + * @param rejectUrl + * + * @return the nominated invitation which will contain the invitationId and + * ticket which will uniqely identify this invitation for the rest + * of the workflow. + * + * @throws InvitationException + * @throws InvitationExceptionUserError + * @throws InvitationExceptionForbidden + */ + public NominatedInvitation inviteNominated(String inviteeFirstName, + String inviteeLastName, String inviteeEmail, + String inviteeUserName, Invitation.ResourceType resourceType, + String resourceName, String inviteeRole, String serverPath, + String acceptUrl, String rejectUrl) + { + // Validate the request + + // Check resource exists + + if (resourceType == Invitation.ResourceType.WEB_SITE) + { + return startInvite(inviteeFirstName, inviteeLastName, inviteeEmail, + inviteeUserName, resourceType, resourceName, inviteeRole, + serverPath, acceptUrl, rejectUrl); + } + + throw new InvitationException("unknown resource type"); + } + + /** + * Start the invitation process for a ModeratedInvitation + * + * @param comments + * why does the invitee want access to the resource ? + * @param inviteeUserName + * who is to be invited + * @param Invitation + * .ResourceType resourceType what resource type ? + * @param resourceName + * which resource + * @param inviteeRole + * which role ? + */ + public ModeratedInvitation inviteModerated(String inviteeComments, + String inviteeUserName, Invitation.ResourceType resourceType, + String resourceName, String inviteeRole) + { + if (resourceType == Invitation.ResourceType.WEB_SITE) + { + return startInvite(inviteeComments, inviteeUserName, resourceType, + resourceName, inviteeRole); + } + throw new InvitationException("unknown resource type"); + } + + /** + * Invitee accepts this invitation + * + * Nominated Invitaton process only + * + * @param invitationId the invitation id + * @param ticket the ticket produced when creating the invitation. + */ + public Invitation accept(String invitationId, String ticket) { + Invitation invitation = getInvitation(invitationId); + + if (invitation instanceof NominatedInvitation) { + + // Check invitationId and ticket match + if (ticket == null + || (!ticket.equals(((NominatedInvitation) invitation) + .getTicket()))) { + throw new InvitationException( + "Response to invite has supplied an invalid ticket. The response to the " + + "invitation could thus not be processed"); + } + + /** + * Nominated invitation complete the wf:invitePendingTask along the + * 'accept' transition because the invitation has been accepted + */ + InviteHelper + .completeInviteTask( + invitationId, + WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_PENDING, + WorkflowModelNominatedInvitation.WF_TRANSITION_ACCEPT, + this.workflowService); + + return invitation; + } + throw new InvitationException( + "State error, cannot call accept a moderated invitation"); + + } + + /** + * Moderator approves this invitation + * + * @param request the request to approve + * @param reason comments about the acceptance + */ + public Invitation approve(String invitationId, String reason) { + Invitation invitation = getInvitation(invitationId); + if(invitation instanceof ModeratedInvitation) + { + // Check approver is a site manager + String approverUserName = this.authenticationService.getCurrentUserName(); + checkManagerRole(approverUserName, invitation.getResourceType(), invitation.getResourceName()); + + WorkflowTaskQuery wfModeratedTaskQuery = new WorkflowTaskQuery(); + + // Current Review Moderated Tasks + wfModeratedTaskQuery.setActive(Boolean.TRUE); + wfModeratedTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfModeratedTaskQuery.setTaskName(WorkflowModelModeratedInvitation.WF_REVIEW_TASK); + wfModeratedTaskQuery.setProcessName(WorkflowModelModeratedInvitation.WF_PROCESS_INVITATION_MODERATED); + + // query for invite review tasks + List wf_moderated_tasks = this.workflowService + .queryTasks(wfModeratedTaskQuery); + + for (WorkflowTask workflowTask : wf_moderated_tasks) + { + Map wfReviewProps = new HashMap(); + wfReviewProps.put(ContentModel.PROP_OWNER, approverUserName); + wfReviewProps.put(WorkflowModelModeratedInvitation.WF_PROP_REVIEW_COMMENTS, reason); + workflowService.updateTask(workflowTask.id, wfReviewProps, null, null); + workflowService.endTask(workflowTask.id, WorkflowModelModeratedInvitation.WF_TRANSITION_APPROVE); + } + } + else + { + throw new InvitationException("State error, cannot call approve"); + } + return invitation; + + } + + /** + * User or moderator rejects this request + * + * @param invitationId + * @param reason + * , optional reason for rejection + */ + public Invitation reject(String invitationId, String reason) { + Invitation invitation = getInvitation(invitationId); + + if (invitation instanceof NominatedInvitation) { + + /** + * Nominated invitation complete the wf:invitePendingTask along the + * 'reject' transition because the invitation has been rejected + */ + InviteHelper + .completeInviteTask( + invitationId, + WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_PENDING, + WorkflowModelNominatedInvitation.WF_TRANSITION_REJECT, + this.workflowService); + + return invitation; + } + + if (invitation instanceof ModeratedInvitation) { + WorkflowTaskQuery wfModeratedTaskQuery = new WorkflowTaskQuery(); + HashMap wfQueryModifiedProps = new HashMap(3, 1.0f); + + // Check rejecter is a site manager and throw and exception if not + String rejecterUserName = this.authenticationService.getCurrentUserName(); + checkManagerRole(rejecterUserName, invitation.getResourceType(), invitation.getResourceName()); + + // Current Review Moderated Tasks + wfModeratedTaskQuery.setActive(Boolean.TRUE); + wfModeratedTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfModeratedTaskQuery.setTaskName(WorkflowModelModeratedInvitation.WF_REVIEW_TASK); + wfModeratedTaskQuery.setProcessName(WorkflowModelModeratedInvitation.WF_PROCESS_INVITATION_MODERATED); + + // query for invite review tasks + List wf_moderated_tasks = this.workflowService + .queryTasks(wfModeratedTaskQuery); + + for (WorkflowTask workflowTask : wf_moderated_tasks) + { + Map wfReviewProps = new HashMap(); + wfReviewProps.put(ContentModel.PROP_OWNER, rejecterUserName); + wfReviewProps.put(WorkflowModelModeratedInvitation.WF_PROP_REVIEW_COMMENTS, reason); + workflowService.updateTask(workflowTask.id, wfReviewProps, null, null); + this.workflowService.endTask(workflowTask.id, WorkflowModelModeratedInvitation.WF_TRANSITION_REJECT); + } + + return invitation; + } + + return invitation; + } + + /* + * cancel a pending request + */ + public Invitation cancel(String invitationId) { + Invitation invitation = getInvitation(invitationId); + + if (invitation instanceof NominatedInvitation) { + + // TODO Who is allowed to cancel ?? + + // Should you be allowed to cancel multiple times ? + + // complete the wf:invitePendingTask along the 'cancel' transition + // because the invitation has been cancelled + InviteHelper + .completeInviteTask( + invitationId, + WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_PENDING, + WorkflowModelNominatedInvitation.WF_TRANSITION_CANCEL, + this.workflowService); + } + + if (invitation instanceof ModeratedInvitation) + { + // TODO Who is allowed to cancel ? + workflowService.cancelWorkflow(invitationId); + } + + return invitation; + } + + /** + * Get an invitation from its invitation id + * + * @throws InvitationExceptionNotFound + * the invitation does not exist. + * @return the invitation. + */ + public Invitation getInvitation(String invitationId) { + WorkflowInstance wi = workflowService.getWorkflowById(invitationId); + if (wi == null) { + Object objs[] = { invitationId }; + throw new InvitationExceptionNotFound("invitation.error.not_found", + objs); + } + String workflowName = wi.definition.getName(); + + if (workflowName + .equals(WorkflowModelNominatedInvitation.WORKFLOW_DEFINITION_NAME)) { + // This is a nominated invitation + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + wfTaskQuery.setProcessId(invitationId); + + // filter to find only the start task which contains the properties. + wfTaskQuery.setTaskState(WorkflowTaskState.COMPLETED); + wfTaskQuery + .setTaskName(WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_TO_SITE); + + // query for invite workflow task associate + List inviteStartTasks = workflowService + .queryTasks(wfTaskQuery); + + // should also be 0 or 1 + if (inviteStartTasks.size() < 1) { + return null; + } else { + WorkflowTask task = inviteStartTasks.get(0); + NominatedInvitationImpl result = new NominatedInvitationImpl( + task.properties); + result.setInviteId(invitationId); + return result; + } + } + if (workflowName + .equals(WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME)) { + // This is a moderated invitation + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + wfTaskQuery.setProcessId(invitationId); + + // filter to find only the start task which contains the properties. + wfTaskQuery.setTaskState(WorkflowTaskState.COMPLETED); + wfTaskQuery.setTaskName(WorkflowModelModeratedInvitation.WF_START_TASK); + + List inviteStartTasks = workflowService.queryTasks(wfTaskQuery); + + // should also be 0 or 1 + if (inviteStartTasks.size() < 1) { + // No start task - workflow may have been completed + Object objs[] = { invitationId }; + throw new InvitationExceptionNotFound("invitation.error.not_found", + objs); + } else { + WorkflowTask task = inviteStartTasks.get(0); + ModeratedInvitationImpl result = new ModeratedInvitationImpl( + task.properties); + result.setInviteId(invitationId); + return result; + } + } + + // Unknown workflow type here + return null; + } + + /** + * list Invitations for a specific person/invitee + * + * @param invitee + * alfresco user id of person being invited + */ + public List listPendingInvitationsForInvitee(String invitee) { + InvitationSearchCriteriaImpl crit = new InvitationSearchCriteriaImpl(); + crit.setInvitationType(InvitationSearchCriteria.InvitationType.ALL); + crit.setInvitee(invitee); + return searchInvitation(crit); + } + + /** + * list Invitations for a specific resource + * + * @param resourceType + * @param resourceName + */ + public List listPendingInvitationsForResource( + Invitation.ResourceType resourceType, String resourceName) { + InvitationSearchCriteriaImpl crit = new InvitationSearchCriteriaImpl(); + crit.setInvitationType(InvitationSearchCriteria.InvitationType.ALL); + crit.setResourceType(resourceType); + crit.setResourceName(resourceName); + return searchInvitation(crit); + } + + /** + * This is the general search invitation method + * + * @param criteria + * @return the list of invitations + */ + public List searchInvitation(InvitationSearchCriteria criteria) { + + List ret = new ArrayList(); + + // at least one of 'inviterUserName', + // 'inviteeUserName', 'siteShortName', + // URL request parameters has not been provided + if (!(criteria.getInvitee() != null + || criteria.getResourceName() != null + || criteria.getInviter() != null)) + { + Object[] objs = {}; + throw new InvitationExceptionUserError( + "search invitation: At least one of the following URL request parameters must be provided in URL " + + "'invite', 'inviter', 'resourceName'", objs); + } + + InvitationSearchCriteria.InvitationType toSearch = criteria.getInvitationType(); + + /** + * Nominated search below + */ + if(toSearch == InvitationSearchCriteria.InvitationType.ALL || toSearch == InvitationSearchCriteria.InvitationType.NOMINATED) + { + // query for nominated workflow tasks by given parameters + // create workflow task query + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + + // the invite URL request + // parameters + // - because this web script class will terminate with a web script + // exception if none of the required + // request parameters are provided, at least one of these query + // properties will be set + // at this point + + // workflow query properties + HashMap wfNominatedQueryProps = new HashMap(10, + 1.0f); + + if (criteria.getInviter() != null) { + wfNominatedQueryProps + .put( + WorkflowModelNominatedInvitation.WF_PROP_INVITER_USER_NAME, + criteria.getInviter()); + } + if (criteria.getInvitee() != null) { + wfNominatedQueryProps + .put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_USER_NAME, + criteria.getInvitee()); + } + if (criteria.getResourceName() != null) { + wfNominatedQueryProps.put( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME, + criteria.getResourceName()); + + wfNominatedQueryProps.put( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE, + criteria.getResourceType().toString()); + } + + // set workflow task query parameters + wfTaskQuery.setProcessCustomProps(wfNominatedQueryProps); + + // query only active workflows + wfTaskQuery.setActive(Boolean.TRUE); + + // pick up the start task + wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfTaskQuery.setTaskName(WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_PENDING); + wfTaskQuery.setProcessName(WorkflowModelNominatedInvitation.WF_PROCESS_INVITE); + + // query for invite workflow tasks + List wf_invite_tasks = this.workflowService + .queryTasks(wfTaskQuery); + + for (WorkflowTask workflowTask : wf_invite_tasks) { + // get workflow instance (ID) that pendingInvite task (in query + // result set) + + String workflowId = workflowTask.path.instance.id; + //TODO ALFCOM-2597 workflowTask.properties does not contain custom process values + // NominatedInvitationImpl result = new NominatedInvitationImpl(workflowTask.properties); + // result.setInviteId(workflowId); + // ret.add(result); + + Invitation result = getInvitation(workflowId); + + // TODO ALFCOM-2598 records are being returned that do not match properties + Setkeys = wfNominatedQueryProps.keySet(); + boolean crap = false; + for(QName key : keys) + { + if(key.equals(WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME)) + { + Object val1 = wfNominatedQueryProps.get(key); + Object val2 = result.getResourceName(); + if (!val1.equals(val2)) + { + // Uh oh ... crap detected + crap = true; + System.out.println("ALFCOM-2598 key:" + key + "query:" + val1 + "task:" + val2); + break; + } + } + if(key.equals(WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE)) + { + Object val1 = wfNominatedQueryProps.get(key); + Object val2 = result.getResourceType().toString(); + if (!val1.equals(val2)) + { + + // Uh oh ... crap detected + crap = true; + System.out.println("ALFCOM-2598 key:" + key + "query:" + val1 + "task:" + val2); + break; + } + } + } + + if(!crap) + { + ret.add(result); + } + } + } + + /** + * Moderated search below + */ + if(toSearch == InvitationSearchCriteria.InvitationType.ALL || toSearch == InvitationSearchCriteria.InvitationType.MODERATED) + { + // This is a moderated search + WorkflowTaskQuery wfModeratedTaskQuery = new WorkflowTaskQuery(); + // workflow query properties + HashMap wfQueryModeratedProps = new HashMap(3, 1.0f); + + if (criteria.getInvitee() != null) + { + wfQueryModeratedProps.put( + WorkflowModelModeratedInvitation.WF_PROP_INVITEE_USER_NAME, + criteria.getInvitee()); + } + if (criteria.getResourceName() != null) + { + wfQueryModeratedProps.put( + WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_NAME, + criteria.getResourceName()); + wfQueryModeratedProps.put( + WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE, + criteria.getResourceType().toString()); + } + + // set workflow task query parameters + wfModeratedTaskQuery.setProcessCustomProps(wfQueryModeratedProps); + + // Current Review Moderated Tasks + wfModeratedTaskQuery.setActive(Boolean.TRUE); + wfModeratedTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfModeratedTaskQuery.setTaskName(WorkflowModelModeratedInvitation.WF_REVIEW_TASK); + wfModeratedTaskQuery.setProcessName(WorkflowModelModeratedInvitation.WF_PROCESS_INVITATION_MODERATED); + + // query for invite workflow tasks + List wf_moderated_tasks = this.workflowService + .queryTasks(wfModeratedTaskQuery); + + for (WorkflowTask workflowTask : wf_moderated_tasks) + { + // Add moderated invitations + String workflowId = workflowTask.path.instance.id; + ModeratedInvitationImpl result = new ModeratedInvitationImpl(workflowTask.properties); + + // TODO ALFCOM-2598 records are being returned that do not match properties + Setkeys = wfQueryModeratedProps.keySet(); + boolean crap = false; + for(QName key : keys) + { + Object val1 = wfQueryModeratedProps.get(key); + Object val2 = workflowTask.properties.get(key); + if(!val1.equals(val2)) + { + // crap detected + crap = true; + System.out.println("ALFCOM-2598 key:" + key + "query:" + val1 + "task:" + val2); + break; + } + } + //TODO END ALFCOM-2598 Work-around + + result.setInviteId(workflowId); + if(!crap) + { + ret.add(result); + } + } + } + + // End moderated invitation + + return ret; + } + + // Implementation methods below + /** + * Services + */ + private WorkflowService workflowService; + private PersonService personService; + private SiteService siteService; + private AuthenticationService authenticationService; + private PermissionService permissionService; + private MutableAuthenticationDao mutableAuthenticationDao; + private NamespaceService namespaceService; + private NodeService nodeService; + // user name and password generation beans + private UserNameGenerator usernameGenerator; + private PasswordGenerator passwordGenerator; + + /** + * Set the workflow service + * + * @param workflowService + */ + public void setWorkflowService(WorkflowService workflowService) { + this.workflowService = workflowService; + } + + /** + * @return the workflow service + */ + public WorkflowService getWorkflowService() { + return workflowService; + } + + public void setPersonService(PersonService personService) { + this.personService = personService; + } + + public PersonService getPersonService() { + return personService; + } + + public void setSiteService(SiteService siteService) { + this.siteService = siteService; + } + + public SiteService getSiteService() { + return siteService; + } + + public void setAuthenticationService( + AuthenticationService authenticationService) { + this.authenticationService = authenticationService; + } + + public AuthenticationService getAuthenticationService() { + return authenticationService; + } + + public void setUserNameGenerator(UserNameGenerator usernameGenerator) { + this.usernameGenerator = usernameGenerator; + } + + public UserNameGenerator getUserNameGenerator() { + return usernameGenerator; + } + + public void setPasswordGenerator(PasswordGenerator passwordGenerator) { + this.passwordGenerator = passwordGenerator; + } + + public PasswordGenerator getPasswordGenerator() { + return passwordGenerator; + } + + public void setNamespaceService(NamespaceService namespaceService) { + this.namespaceService = namespaceService; + } + + public NamespaceService getNamespaceService() { + return namespaceService; + } + + public void setPermissionService(PermissionService permissionService) { + this.permissionService = permissionService; + } + + public PermissionService getPermissionService() { + return permissionService; + } + + public void setMutableAuthenticationDao( + MutableAuthenticationDao mutableAuthenticationDao) { + this.mutableAuthenticationDao = mutableAuthenticationDao; + } + + public MutableAuthenticationDao getMutableAuthenticationDao() { + return mutableAuthenticationDao; + } + + public void setNodeService(NodeService nodeService) { + this.nodeService = nodeService; + } + + public NodeService getNodeService() { + return nodeService; + } + + /** + * Creates a person for the invitee with a generated user name. + * + * @param inviteeFirstName + * first name of invitee + * @param inviteeLastName + * last name of invitee + * @param inviteeEmail + * email address of invitee + * @return invitee user name + */ + private String createInviteePerson(String inviteeFirstName, + String inviteeLastName, String inviteeEmail) { + // Attempt to generate user name for invitee + // which does not belong to an existing person + // Tries up to MAX_NUM_INVITEE_USER_NAME_GEN_TRIES + // at which point a web script exception is thrown + String inviteeUserName = null; + int i = 0; + do { + inviteeUserName = usernameGenerator.generateUserName(); + i++; + } while (this.personService.personExists(inviteeUserName) + && (i < MAX_NUM_INVITEE_USER_NAME_GEN_TRIES)); + + // if after 10 tries is not able to generate a user name for a + // person who doesn't already exist, then throw a web script exception + if (this.personService.personExists(inviteeUserName)) { + + logger.debug("Failed - unable to generate username for invitee."); + + Object[] objs = { inviteeFirstName, inviteeLastName, inviteeEmail }; + throw new InvitationException( + "invitation.invite.unable_generate_id", objs); + } + + // create a person node for the invitee with generated invitee user name + // and other provided person property values + final Map properties = new HashMap(); + properties.put(ContentModel.PROP_USERNAME, inviteeUserName); + properties.put(ContentModel.PROP_FIRSTNAME, inviteeFirstName); + properties.put(ContentModel.PROP_LASTNAME, inviteeLastName); + properties.put(ContentModel.PROP_EMAIL, inviteeEmail); + + final String finalUserName = inviteeUserName; + AuthenticationUtil.runAs(new RunAsWork() { + public Object doWork() throws Exception { + NodeRef person = personService.createPerson(properties); + permissionService.setPermission(person, finalUserName, + PermissionService.ALL_PERMISSIONS, true); + + return null; + } + + }, AuthenticationUtil.getSystemUserName()); + + return inviteeUserName; + } + + /** + * Creates a disabled user account for the given invitee user name with a + * generated password + * + * @param inviteeUserName + * @return password generated for invitee user account + */ + private String createInviteeDisabledAccount(String inviteeUserName) { + // generate password using password generator + char[] generatedPassword = passwordGenerator.generatePassword() + .toCharArray(); + + // create disabled user account for invitee user name with generated + // password + this.mutableAuthenticationDao.createUser(inviteeUserName, + generatedPassword); + this.mutableAuthenticationDao.setEnabled(inviteeUserName, false); + + return String.valueOf(generatedPassword); + } + + /** + * Moderated invitation implementation + * + * @param inviteeComments + * @param inviteeUserName + * @param resourceType + * @param resourceName + * @param inviteeRole + * @return the new moderated invitation + */ + private ModeratedInvitation startInvite(String inviteeComments, + String inviteeUserName, Invitation.ResourceType resourceType, + String resourceName, String inviteeRole) + { + + // Get invitee person NodeRef to add as assignee + NodeRef inviteeNodeRef = this.personService.getPerson(inviteeUserName); + + 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); + + NodeRef wfPackage = this.workflowService.createPackage(null); + + Map workflowProps = new HashMap( + 16); + workflowProps.put(WorkflowModel.ASSOC_PACKAGE, wfPackage); + workflowProps.put(WorkflowModel.ASSOC_ASSIGNEE, inviteeNodeRef); + 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( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME, + resourceName); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE, + resourceType.toString()); + + // get the moderated workflow + + WorkflowDefinition wfDefinition = this.workflowService + .getDefinitionByName(WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME); + if (wfDefinition == null) { + // handle workflow definition does not exist + Object objs[] = { WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME }; + throw new InvitationException("invitation.error.noworkflow", objs); + } + + // start the workflow + WorkflowPath wfPath = this.workflowService.startWorkflow(wfDefinition + .getId(), workflowProps); + + String workflowId = wfPath.instance.id; + String wfPathId = wfPath.id; + List wfTasks = this.workflowService + .getTasksForWorkflowPath(wfPathId); + + // throw an exception if no tasks where found on the workflow path + if (wfTasks.size() == 0) + { + Object objs[] = { WorkflowModelModeratedInvitation.WORKFLOW_DEFINITION_NAME }; + throw new InvitationException("invitation.error.notasks", objs); + } + + try { + WorkflowTask wfStartTask = wfTasks.get(0); + this.workflowService.endTask(wfStartTask.id, null); + } catch (RuntimeException err) { + if (logger.isDebugEnabled()) + logger.debug("Failed - caught error during Invite workflow transition: " + + err.getMessage()); + throw err; + } + + ModeratedInvitationImpl result = new ModeratedInvitationImpl( + workflowProps); + result.setInviteId(workflowId); + return result; + } + + /** + * Starts the Invite workflow + * + * @param inviteeFirstName + * first name of invitee + * @param inviteeLastNamme + * last name of invitee + * @param inviteeEmail + * email address of invitee + * @param siteShortName + * short name of site that the invitee is being invited to by the + * inviter + * @param inviteeSiteRole + * role under which invitee is being invited to the site by the + * inviter + * @param serverPath + * externally accessible server address of server hosting invite + * web scripts + */ + private NominatedInvitation startInvite(String inviteeFirstName, + String inviteeLastName, String inviteeEmail, + String inviteeUserName, Invitation.ResourceType resourceType, + String siteShortName, String inviteeSiteRole, String serverPath, + String acceptUrl, String rejectUrl) { + + // get the inviter user name (the name of user web script is executed + // under) + String inviterUserName = this.authenticationService + .getCurrentUserName(); + + checkManagerRole(inviterUserName, resourceType, siteShortName); + + if (logger.isDebugEnabled()) { + logger.debug("startInvite() inviterUserName=" + inviterUserName + + " inviteeUserName=" + inviteeUserName + + " inviteeFirstName=" + inviteeFirstName + + " inviteeLastName=" + inviteeLastName + " inviteeEmail=" + + inviteeEmail + " siteShortName=" + siteShortName + + " inviteeSiteRole=" + inviteeSiteRole); + } + // + // if we have not explicitly been passed an existing user's user name + // then .... + // + // if a person already exists who has the given invitee email address + // + // 1) obtain invitee user name from first person found having the + // invitee email address (there + // should only be one) + // 2) handle error conditions - (invitee already has an invitation in + // progress for the given site, + // or he/she is already a member of the given site + // + if (inviteeUserName == null || inviteeUserName.trim().length() == 0) { + Set peopleWithInviteeEmail = this.personService + .getPeopleFilteredByProperty(ContentModel.PROP_EMAIL, + inviteeEmail); + if (peopleWithInviteeEmail.isEmpty() == false) { + // get person already existing who has the given + // invitee email address (there should only be one, so just take + // the first from the set of people). + NodeRef person = (NodeRef) peopleWithInviteeEmail.toArray()[0]; + + // get invitee user name of that person + Serializable userNamePropertyVal = this.getNodeService() + .getProperty(person, ContentModel.PROP_USERNAME); + inviteeUserName = DefaultTypeConverter.INSTANCE.convert( + String.class, userNamePropertyVal); + + if (logger.isDebugEnabled()) + logger + .debug("not explictly passed username - found matching email, resolved inviteeUserName=" + + inviteeUserName); + } + // else there are no existing people who have the given invitee + // email address + // so create invitee person + else { + inviteeUserName = createInviteePerson(inviteeFirstName, + inviteeLastName, inviteeEmail); + + if (logger.isDebugEnabled()) + logger + .debug("not explictly passed username - created new person, inviteeUserName=" + + inviteeUserName); + } + } + + // throw web script exception if person is already a member of the given + // site + if (this.siteService.isMember(siteShortName, inviteeUserName)) { + if (logger.isDebugEnabled()) + logger + .debug("Failed - invitee user is already a member of the site."); + + Object objs[] = { inviteeUserName, inviteeEmail, siteShortName }; + throw new InvitationExceptionUserError( + "invitation.invite.already_member", objs); + } + + // + // If a user account does not already exist for invitee user name + // then create a disabled user account for the invitee. + // Hold a local reference to generated password if disabled invitee + // account + // is created, otherwise if a user account already exists for invitee + // user name, then local reference to invitee password will be "null" + // + String inviteePassword = null; + if (this.mutableAuthenticationDao.userExists(inviteeUserName) == false) { + if (logger.isDebugEnabled()) + logger + .debug("Invitee user account does not exist, creating disabled account."); + inviteePassword = createInviteeDisabledAccount(inviteeUserName); + } + + // create a ticket for the invite - this is used + String inviteTicket = GUID.generate(); + + // + // Start the invite workflow with inviter, invitee and site properties + // + + WorkflowDefinition wfDefinition = this.workflowService + .getDefinitionByName(WorkflowModelNominatedInvitation.WORKFLOW_DEFINITION_NAME); + + if (wfDefinition == null) { + // handle workflow definition does not exist + Object objs[] = { WorkflowModelNominatedInvitation.WORKFLOW_DEFINITION_NAME }; + throw new InvitationException("invitation.error.noworkflow", objs); + } + + // Get invitee person NodeRef to add as assignee + NodeRef inviteeNodeRef = this.personService.getPerson(inviteeUserName); + + // create workflow properties + Map workflowProps = new HashMap( + 16); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITER_USER_NAME, + inviterUserName); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_USER_NAME, + inviteeUserName); + workflowProps.put(WorkflowModel.ASSOC_ASSIGNEE, inviteeNodeRef); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_FIRSTNAME, + inviteeFirstName); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_LASTNAME, + inviteeLastName); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_GEN_PASSWORD, + inviteePassword); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME, + siteShortName); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE, + resourceType.toString()); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_SITE_ROLE, + inviteeSiteRole); + workflowProps.put(WorkflowModelNominatedInvitation.WF_PROP_SERVER_PATH, + serverPath); + workflowProps.put(WorkflowModelNominatedInvitation.WF_PROP_ACCEPT_URL, + acceptUrl); + workflowProps.put(WorkflowModelNominatedInvitation.WF_PROP_REJECT_URL, + rejectUrl); + workflowProps.put( + WorkflowModelNominatedInvitation.WF_PROP_INVITE_TICKET, + inviteTicket); + + // start the workflow + WorkflowPath wfPath = this.workflowService.startWorkflow(wfDefinition + .getId(), workflowProps); + + // + // complete invite workflow start task to send out the invite email + // + + // get the workflow tasks + String workflowId = wfPath.instance.id; + String wfPathId = wfPath.id; + List wfTasks = this.workflowService + .getTasksForWorkflowPath(wfPathId); + + // throw an exception if no tasks where found on the workflow path + if (wfTasks.size() == 0) { + Object objs[] = { WorkflowModelNominatedInvitation.WORKFLOW_DEFINITION_NAME }; + throw new InvitationException("invitation.error.notasks", objs); + } + + // + // first task in workflow task list (there should only be one) + // associated + // with the workflow path id (above) should be "wf:inviteToSiteTask", + // otherwise + // throw web script exception + // + String wfTaskName = wfTasks.get(0).name; + QName wfTaskNameQName = QName.createQName(wfTaskName, + this.namespaceService); + QName inviteToSiteTaskQName = WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_TO_SITE; + if (!wfTaskNameQName.equals(inviteToSiteTaskQName)) { + Object objs[] = { + wfPathId, + WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_TO_SITE }; + throw new InvitationException("invitation.error.wrong_first_task", + objs); + } + + // get "inviteToSite" task + WorkflowTask wfStartTask = wfTasks.get(0); + + // attach empty package to start task, end it and follow with transition + // that sends out the invite + if (logger.isDebugEnabled()) + logger + .debug("Starting Invite workflow task by attaching empty package..."); + NodeRef wfPackage = this.workflowService.createPackage(null); + Map wfTaskProps = new HashMap( + 1, 1.0f); + wfTaskProps.put(WorkflowModel.ASSOC_PACKAGE, wfPackage); + + if (logger.isDebugEnabled()) + logger.debug("Updating Invite workflow task..."); + this.workflowService + .updateTask(wfStartTask.id, wfTaskProps, null, null); + + if (logger.isDebugEnabled()) + logger.debug("Transitioning Invite workflow task..."); + try { + this.workflowService.endTask(wfStartTask.id, + WorkflowModelNominatedInvitation.WF_TRANSITION_SEND_INVITE); + } catch (RuntimeException err) { + if (logger.isDebugEnabled()) + logger + .debug("Failed - caught error during Invite workflow transition: " + + err.getMessage()); + throw err; + } + + NominatedInvitationImpl result = new NominatedInvitationImpl( + workflowProps); + result.setTicket(inviteTicket); + result.setInviteId(workflowId); + return result; + } + + + /** + * Check that the specified user has manager role over the resource. + * @param userId + * @throws InvitationException + */ + private void checkManagerRole(String userId, Invitation.ResourceType resourceType, String siteShortName) + { + // if inviter is not the site manager then throw web script exception + String inviterRole = this.siteService.getMembersRole(siteShortName, + userId); + if ((inviterRole == null) + || (inviterRole.equals(SiteModel.SITE_MANAGER) == false)) { + + Object objs[] = { userId, siteShortName }; + throw new InvitationExceptionForbidden( + "invitation.invite.not_site_manager", objs); + } + } +} diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java new file mode 100644 index 0000000000..011ff80148 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImplTest.java @@ -0,0 +1,739 @@ +/* + * 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 recieved 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.invitation; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.service.cmr.invitation.InvitationExceptionUserError; +import org.alfresco.service.cmr.invitation.InvitationSearchCriteria; +import org.alfresco.service.cmr.invitation.InvitationService; +import org.alfresco.service.cmr.invitation.Invitation; +import org.alfresco.service.cmr.invitation.ModeratedInvitation; +import org.alfresco.service.cmr.invitation.NominatedInvitation; +import org.alfresco.service.cmr.invitation.Invitation.ResourceType; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +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.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.util.BaseAlfrescoSpringTest; +import org.alfresco.util.PropertyMap; + +import junit.framework.TestCase; + +/** + * + * Unit tests of Invitation Service + * + */ +public class InvitationServiceImplTest extends BaseAlfrescoSpringTest +{ + private SiteService siteService; + private AuthenticationComponent authenticationComponent; + private PersonService personService; + private InvitationService invitationService; + private MutableAuthenticationDao mutableAuthenticationDao; + + private final String SITE_SHORT_NAME_INVITE = "InvitationTest"; + private final String SITE_SHORT_NAME_RED = "InvitationTestRed"; + private final String SITE_SHORT_NAME_BLUE = "InvitationTestBlue"; + public static String PERSON_FIRSTNAME = "InvitationFirstName123"; + public static String PERSON_LASTNAME = "InvitationLastName123"; + public static String PERSON_JOBTITLE = "JobTitle123"; + public static String PERSON_ORG = "Organisation123"; + + public static String USER_MANAGER = "InvitationServiceManagerOne"; + public static String USER_ONE = "InvitationServiceAlice"; + public static String USER_TWO = "InvitationServiceBob"; + public static String USER_EVE = "InvitationServiceEve"; + + /** + * Called during the transaction setup + */ + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + this.invitationService = (InvitationService)this.applicationContext.getBean("InvitationService"); + this.siteService = (SiteService)this.applicationContext.getBean("SiteService"); + this.personService = (PersonService)this.applicationContext.getBean("PersonService"); + this.authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent"); + this.mutableAuthenticationDao = (MutableAuthenticationDao)this.applicationContext.getBean("authenticationDao"); + + createPerson(USER_MANAGER, ""); + createPerson(USER_ONE, ""); + createPerson(USER_TWO, ""); + createPerson(USER_EVE, ""); + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + + SiteInfo siteInfo = siteService.getSite(SITE_SHORT_NAME_INVITE); + if (siteInfo == null) + { + siteService.createSite("InviteSitePreset", + SITE_SHORT_NAME_INVITE, + "InviteSiteTitle", + "InviteSiteDescription", + SiteVisibility.MODERATED); + } + + SiteInfo siteInfoRed = siteService.getSite(SITE_SHORT_NAME_RED); + if (siteInfoRed == null) + { + siteService.createSite("InviteSiteRed", + SITE_SHORT_NAME_RED, + "InviteSiteTitle", + "InviteSiteDescription", + SiteVisibility.MODERATED); + } + SiteInfo siteInfoBlue = siteService.getSite(SITE_SHORT_NAME_BLUE); + if (siteInfoBlue == null) + { + siteService.createSite("InviteSiteBlue", + SITE_SHORT_NAME_BLUE, + "InviteSiteTitle", + "InviteSiteDescription", + SiteVisibility.MODERATED); + } + + + + } + + protected void onTearDownInTransaction() throws Exception + { + super.onTearDownInTransaction(); + this.authenticationComponent.setSystemUserAsCurrentUser(); + siteService.deleteSite(SITE_SHORT_NAME_INVITE); + siteService.deleteSite(SITE_SHORT_NAME_RED); + siteService.deleteSite(SITE_SHORT_NAME_BLUE); + deletePersonByUserName(USER_ONE); + deletePersonByUserName(USER_TWO); + deletePersonByUserName(USER_EVE); + deletePersonByUserName(USER_MANAGER); + } + + /* + * end of setup now for some real tests + */ + + /** + * + */ + public void testConfiguration() + { + assertNotNull("Invitation service is null", invitationService); + } + + /** + * Create a Nominated Invitation + * read it. + * search for it + * cancel it + * search for it again (and fail to find it) + * Create a Nominated Invitation + * read it. + * search for it + * reject it + * Create a Nominated Invitation + * read it. + * accept it + */ + public void testNominatedInvitation() throws Exception + { + String inviteeFirstName = PERSON_FIRSTNAME; + String inviteeLastName = PERSON_LASTNAME; + String inviteeEmail = "123"; + String inviteeUserName = "invitee@alfrescotesting.com"; + Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE; + String resourceName = SITE_SHORT_NAME_INVITE; + String inviteeRole = SiteModel.SITE_COLLABORATOR; + String serverPath = "wibble"; + String acceptUrl = "froob"; + String rejectUrl = "marshmallow"; + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + + NominatedInvitation nominatedInvitation = invitationService.inviteNominated(inviteeFirstName, + inviteeLastName, + inviteeEmail, + inviteeUserName, + resourceType, + resourceName, + inviteeRole, + serverPath, + acceptUrl, + rejectUrl) ; + + assertNotNull("nominated invitation is null", nominatedInvitation); + String inviteId = nominatedInvitation.getInviteId(); + assertEquals("first name wrong", inviteeFirstName, nominatedInvitation.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, nominatedInvitation.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, nominatedInvitation.getInviteeUserName()); + assertEquals("resource type name wrong", resourceType, nominatedInvitation.getResourceType()); + assertEquals("resource name wrong", resourceName, nominatedInvitation.getResourceName()); + assertEquals("role name wrong", inviteeRole, nominatedInvitation.getRoleName()); + assertEquals("server path wrong", serverPath, nominatedInvitation.getServerPath()); + assertEquals("accept URL wrong", acceptUrl, nominatedInvitation.getAcceptUrl()); + assertEquals("reject URL wrong", rejectUrl, nominatedInvitation.getRejectUrl()); + + /** + * Now we have an invitation get it and check the details have been returned correctly. + */ + NominatedInvitation invitation = (NominatedInvitation)invitationService.getInvitation(inviteId); + + assertNotNull("invitation is null", invitation); + assertEquals("invite id wrong", inviteId, invitation.getInviteId()); + assertEquals("first name wrong", inviteeFirstName, invitation.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, invitation.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, invitation.getInviteeUserName()); + assertEquals("resource type name wrong", resourceType, invitation.getResourceType()); + assertEquals("resource name wrong", resourceName, invitation.getResourceName()); + assertEquals("role name wrong", inviteeRole, invitation.getRoleName()); + assertEquals("server path wrong", serverPath, invitation.getServerPath()); + assertEquals("accept URL wrong", acceptUrl, invitation.getAcceptUrl()); + assertEquals("reject URL wrong", rejectUrl, invitation.getRejectUrl()); + + /** + * Search for the new invitation + */ + List invitations = invitationService.listPendingInvitationsForResource(resourceType, resourceName); + assertTrue("invitations is empty", !invitations.isEmpty()); + + NominatedInvitation firstInvite = (NominatedInvitation)invitations.get(0); + assertEquals("invite id wrong", inviteId, firstInvite.getInviteId()); + assertEquals("first name wrong", inviteeFirstName, firstInvite.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, firstInvite.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, firstInvite.getInviteeUserName()); + + /** + * Now cancel the invitation + */ + NominatedInvitation canceledInvitation = (NominatedInvitation)invitationService.cancel(inviteId); + assertEquals("invite id wrong", inviteId, canceledInvitation.getInviteId()); + assertEquals("first name wrong", inviteeFirstName, canceledInvitation.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, canceledInvitation.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, canceledInvitation.getInviteeUserName()); + + /** + * Do the query again - should no longer find anything + */ + List it2 = invitationService.listPendingInvitationsForResource(resourceType, resourceName); + assertTrue("invitations is not empty", it2.isEmpty()); + + /** + * Now invite and reject + */ + NominatedInvitation secondInvite = invitationService.inviteNominated(inviteeFirstName, + inviteeLastName, + inviteeEmail, + inviteeUserName, + resourceType, + resourceName, + inviteeRole, + serverPath, + acceptUrl, + rejectUrl) ; + + NominatedInvitation rejectedInvitation = (NominatedInvitation)invitationService.cancel(secondInvite.getInviteId()); + assertEquals("invite id wrong", secondInvite.getInviteId(), rejectedInvitation.getInviteId()); + assertEquals("first name wrong", inviteeFirstName, rejectedInvitation.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, rejectedInvitation.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, rejectedInvitation.getInviteeUserName()); + + List it3 = invitationService.listPendingInvitationsForResource(resourceType, resourceName); + assertTrue("invitations is not empty", it3.isEmpty()); + + /** + * Now invite and accept + */ + NominatedInvitation thirdInvite = invitationService.inviteNominated(inviteeFirstName, + inviteeLastName, + inviteeEmail, + inviteeUserName, + resourceType, + resourceName, + inviteeRole, + serverPath, + acceptUrl, + rejectUrl) ; + + NominatedInvitation acceptedInvitation = (NominatedInvitation)invitationService.accept(thirdInvite.getInviteId(), thirdInvite.getTicket()); + assertEquals("invite id wrong", thirdInvite.getInviteId(), acceptedInvitation.getInviteId()); + assertEquals("first name wrong", inviteeFirstName, acceptedInvitation.getInviteeFirstName()); + assertEquals("last name wrong", inviteeLastName, acceptedInvitation.getInviteeLastName()); + assertEquals("user name wrong", inviteeUserName, acceptedInvitation.getInviteeUserName()); + + List it4 = invitationService.listPendingInvitationsForResource(resourceType, resourceName); + assertTrue("invitations is not empty", it4.isEmpty()); + + /** + * Now verify access control list + */ + String roleName = siteService.getMembersRole(resourceName, inviteeUserName); + assertEquals("role name wrong", roleName, inviteeRole); + siteService.removeMembership(resourceName, inviteeUserName); + } + + /** + * Create a moderated invitation + * Get it + * Search for it + * Cancel it + * + * Create a moderated invitation + * Reject the invitation + * + * Create a moderated invitation + * Approve the invitation + */ + public void testModeratedInvitation() + { + 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); + + 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()); + + /** + * 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 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 inv2 = invitationService.listPendingInvitationsForResource(resourceType, resourceName); + assertTrue("After cancel invitations is not empty", inv2.isEmpty()); + + /** + * New invitation + */ + this.authenticationComponent.setCurrentUser(USER_TWO); + ModeratedInvitation invite2 = invitationService.inviteModerated(comments, + inviteeUserName, + resourceType, + resourceName, + inviteeRole); + + String secondInvite = invite2.getInviteId(); + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + invitationService.reject(secondInvite, "This is a test reject"); + + /** + * New invitation + */ + this.authenticationComponent.setCurrentUser(USER_TWO); + ModeratedInvitation invite3 = invitationService.inviteModerated(comments, + inviteeUserName, + resourceType, + resourceName, + inviteeRole); + + 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", roleName, inviteeRole); + siteService.removeMembership(resourceName, inviteeUserName); + + } + + /** + * Test the approval of a moderated invitation + */ + public void testModeratedApprove() + { + 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!"; + + /** + * New invitation from User TWO + */ + this.authenticationComponent.setCurrentUser(USER_TWO); + ModeratedInvitation invitation = invitationService.inviteModerated(comments, + inviteeUserName, + resourceType, + resourceName, + inviteeRole); + + String invitationId = invitation.getInviteId(); + + /** + * Negative test + * Attempt to approve without the necessary role + */ + try + { + invitationService.approve(invitationId, "No Way Hosea!"); + assertTrue("excetion not thrown", false); + + } + catch (Exception e) + { + // An exception should have been thrown + e.printStackTrace(); + System.out.println(e.toString()); + } + + /** + * Approve the invitation + */ + this.authenticationComponent.setCurrentUser(USER_MANAGER); + invitationService.approve(invitationId, "Come on in"); + + /** + * Now verify access control list contains user two + */ + String roleName = siteService.getMembersRole(resourceName, inviteeUserName); + assertEquals("role name wrong", roleName, inviteeRole); + + /** + * Negative test + * attempt to approve an invitation that has aready been approved + */ + try + { + invitationService.approve(invitationId, "Have I not already done this?"); + assertTrue("duplicate approve excetion not thrown", false); + } + catch (Exception e) + { + // An exception should have been thrown + e.printStackTrace(); + System.out.println(e.toString()); + } + /** + * Negative test + * User is already a member of the site + */ + siteService.removeMembership(resourceName, inviteeUserName); + } + + /** + * Tests of Moderated Reject + */ + public void testModeratedReject() + { + 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!"; + + /** + * New invitation from User TWO + */ + this.authenticationComponent.setCurrentUser(USER_TWO); + ModeratedInvitation invitation = invitationService.inviteModerated(comments, + inviteeUserName, + resourceType, + resourceName, + inviteeRole); + + String invitationId = invitation.getInviteId(); + + /** + * Negative test + * Attempt to reject without the necessary role + */ + try + { + invitationService.approve(invitationId, "No Way Hosea!"); + assertTrue("excetion not thrown", false); + + } + catch (Exception e) + { + // An exception should have been thrown + e.printStackTrace(); + System.out.println(e.toString()); + } + + /** + * Reject the invitation + */ + this.authenticationComponent.setCurrentUser(USER_MANAGER); + invitationService.approve(invitationId, "Go away!"); + + /** + * Negative test + * attempt to approve an invitation that has been rejected + */ + try + { + invitationService.approve(invitationId, "Have I not rejected this?"); + assertTrue("rejected invitation not working", false); + } + catch (Exception e) + { + // An exception should have been thrown + e.printStackTrace(); + System.out.println(e.toString()); + } + } + + /** + * Test search invitation + */ + public void testSearchInvitation() + { + /** + * Make up a tree of invitations and then search + * + * Resource, User, Workflow + * 1) RED, One, Moderated + * 2) RED, One, Nominated + * 3) BLUE, One, Nominated + * 4) RED, Two, Moderated + * + */ + Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE; + String inviteeRole = SiteModel.SITE_COLLABORATOR; + String comments = "please sir, let me in!"; + String inviteeFirstName = PERSON_FIRSTNAME; + String inviteeLastName = PERSON_LASTNAME; + String inviteeEmail = "123"; + String serverPath = "wibble"; + String acceptUrl = "froob"; + String rejectUrl = "marshmallow"; + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + ModeratedInvitation invitationOne = invitationService.inviteModerated(comments, + USER_ONE, + resourceType, + SITE_SHORT_NAME_RED, + inviteeRole); + + String oneId = invitationOne.getInviteId(); + NominatedInvitation invitationTwo = invitationService.inviteNominated(inviteeFirstName, + inviteeLastName, + inviteeEmail, + USER_ONE, + resourceType, + SITE_SHORT_NAME_RED, + inviteeRole, + serverPath, + acceptUrl, + rejectUrl) ; + String twoId = invitationTwo.getInviteId(); + + NominatedInvitation invitationThree = invitationService.inviteNominated(inviteeFirstName, + inviteeLastName, + inviteeEmail, + USER_ONE, + resourceType, + SITE_SHORT_NAME_BLUE, + inviteeRole, + serverPath, + acceptUrl, + rejectUrl) ; + String threeId = invitationThree.getInviteId(); + + ModeratedInvitation invitationFour = invitationService.inviteModerated(comments, + USER_TWO, + resourceType, + SITE_SHORT_NAME_RED, + inviteeRole); + String fourId = invitationFour.getInviteId(); + + /** + * Search for invitations for BLUE + */ + List resOne = invitationService.listPendingInvitationsForResource(ResourceType.WEB_SITE, SITE_SHORT_NAME_BLUE); + assertEquals("blue invites not 1", 1, resOne.size()); + assertEquals("blue id wrong", threeId, resOne.get(0).getInviteId()); + + /** + * Search for invitations for RED + */ + List resTwo = invitationService.listPendingInvitationsForResource(ResourceType.WEB_SITE, SITE_SHORT_NAME_RED); + assertEquals("red invites not 3", 3, resTwo.size()); + + /** + * Search for invitations for USER_ONE + */ + List resThree = invitationService.listPendingInvitationsForInvitee(USER_ONE); + assertEquals("user one does not have 3 invitations", 3, resThree.size()); + + /** + * Search for invitations for USER_TWO + */ + List resFour = invitationService.listPendingInvitationsForInvitee(USER_TWO); + assertEquals("user two does not have 1 invitations", 1, resFour.size()); + + /** + * Search for user1's nominated invitations + */ + InvitationSearchCriteriaImpl crit1 = new InvitationSearchCriteriaImpl(); + crit1.setInvitee(USER_ONE); + crit1.setInvitationType(InvitationSearchCriteria.InvitationType.NOMINATED); + + List resFive = invitationService.searchInvitation(crit1); + assertEquals("user one does not have 2 nominated", 2, resFive.size()); + + + /** + * Negative test - search with an empty criteria + */ + InvitationSearchCriteria crit2 = new InvitationSearchCriteriaImpl(); + try + { + List resSix = invitationService.searchInvitation(crit2); + assertTrue("exception not thrown", false); + } + catch (InvitationExceptionUserError e) + { + // Should go here - no criteria + } + + } + + + /** + * + */ + public void testGetInvitation() + { + try + { + /** + * Get an invitation that does not exist. + */ + invitationService.getInvitation("jbpm$99999999"); + fail("should have thrown an exception"); + } + catch (Exception e) + { + + } + } + + private void createPerson(String userName, String emailAddress) + { + // 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()); + } + + // if person node with given user name doesn't already exist then create + // person + if (this.personService.personExists(userName) == false) + { + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, PERSON_FIRSTNAME); + personProps.put(ContentModel.PROP_LASTNAME, PERSON_LASTNAME); + personProps.put(ContentModel.PROP_EMAIL, emailAddress); + personProps.put(ContentModel.PROP_JOBTITLE, PERSON_JOBTITLE); + personProps.put(ContentModel.PROP_ORGANIZATION, PERSON_ORG); + + // create person node for user + this.personService.createPerson(personProps); + } + } + + private void deletePersonByUserName(String userName) + { + // delete authentication if authentication exists for given user name + if (this.authenticationService.authenticationExists(userName)) + { + this.authenticationService.deleteAuthentication(userName); + } + + // delete user account + if (this.mutableAuthenticationDao.userExists(userName)) + { + this.mutableAuthenticationDao.deleteUser(userName); + } + + // delete person node associated with given user name + // if one exists + if (this.personService.personExists(userName)) + { + this.personService.deletePerson(userName); + } + } +} diff --git a/source/java/org/alfresco/repo/invitation/ModeratedInvitationImpl.java b/source/java/org/alfresco/repo/invitation/ModeratedInvitationImpl.java new file mode 100644 index 0000000000..d3f3b556a4 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/ModeratedInvitationImpl.java @@ -0,0 +1,90 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.service.cmr.invitation.ModeratedInvitation; +import org.alfresco.service.cmr.invitation.Invitation.ResourceType; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; +import java.util.Map; + +/** + * InvitationRequestImpl is a basic InvitationRequest that is processed by the + * InvitationService + */ +/*package scope */ class ModeratedInvitationImpl extends InvitationImpl implements ModeratedInvitation, Serializable +{ + /** + * + */ + private static final long serialVersionUID = -5557544865169876451L; + + private String roleName; + private String inviteeComments; + + public ModeratedInvitationImpl() + { + super(); + } + + public ModeratedInvitationImpl(Map workflowProps) + { + super(); + + setInviteeUserName((String)workflowProps.get(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_USER_NAME)); + setResourceName((String)workflowProps.get(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_NAME)); + if(workflowProps.containsKey(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE)) + { + setResourceType(ResourceType.valueOf((String)workflowProps.get(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE))); + } + roleName = (String)workflowProps.get(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_ROLE); + inviteeComments = (String)workflowProps.get(WorkflowModelModeratedInvitation.WF_PROP_INVITEE_COMMENTS); + + } + + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + public String getRoleName() + { + return roleName; + } + + public String getInviteeComments() + { + return inviteeComments; + } + + public void setInviteeComments(String inviteeComments) + { + this.inviteeComments = inviteeComments; + } + + +} diff --git a/source/java/org/alfresco/repo/invitation/ModeratedInvitationProcess.java b/source/java/org/alfresco/repo/invitation/ModeratedInvitationProcess.java new file mode 100644 index 0000000000..fcb0085a14 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/ModeratedInvitationProcess.java @@ -0,0 +1,63 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.service.cmr.invitation.Invitation; + +/** + * The Moderated Invitation Process has a moderator who approves or rejects + * invitations raised by the invitee themselves. + * + * Upon approval the invitee will be given the requested role for the + * requested resource. + */ + +public interface ModeratedInvitationProcess extends InvitationProcess +{ + /** + * Invitee kicks off process + * @param request + * @param reason + */ + public Invitation invite(Invitation request, String reason); + + /** + * Moderator approves this request + * @param request the request to approve. + */ + public void approve(Invitation request, String reason); + + /** + * Moderator rejects this request + * @param request the request to reject + */ + public void reject(Invitation request, String reason); + + /** + * Invitee cancels this request + */ + public void cancel (Invitation request, String reason); + +} diff --git a/source/java/org/alfresco/repo/invitation/NominatedInvitationImpl.java b/source/java/org/alfresco/repo/invitation/NominatedInvitationImpl.java new file mode 100644 index 0000000000..7ed0d1e01b --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/NominatedInvitationImpl.java @@ -0,0 +1,157 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.invitation.Invitation; +import org.alfresco.service.cmr.invitation.NominatedInvitation; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +/** + * NominatedInvitationImpl is a basic Nominated Invitation Request that + * is processed by the InvitationService. + * + * @see org.alfresco.service.cmr.invitation.NominatedInvitation + */ + +/*package scope */ class NominatedInvitationImpl extends InvitationImpl implements NominatedInvitation, Serializable +{ + + private static final long serialVersionUID = -8800842866845149466L; + private String inviteeFirstName; + private String inviteeLastName; + private String inviteeEmail; + private String roleName; + private String serverPath; + private String acceptUrl; + private String rejectUrl; + private Date sentInviteDate; + private String ticket; + + /** + * create a new nominated invitation + */ + public NominatedInvitationImpl() + { + super(); + } + + public NominatedInvitationImpl(Map workflowProps) + { + super(); + setInviteeUserName((String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_USER_NAME)); + inviteeFirstName = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_FIRSTNAME); + inviteeLastName = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_LASTNAME); +// inviteePassword = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_GEN_PASSWORD); + setResourceName( (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME)); + + if(workflowProps.containsKey(WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE)) + { + setResourceType(ResourceType.valueOf((String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_TYPE))); + } + roleName = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_SITE_ROLE); + serverPath = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_SERVER_PATH); + acceptUrl = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_ACCEPT_URL); + rejectUrl = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_REJECT_URL); + ticket = (String)workflowProps.get(WorkflowModelNominatedInvitation.WF_PROP_INVITE_TICKET); + + } + + public void setInviteeFirstName(String inviteeFirstName) { + this.inviteeFirstName = inviteeFirstName; + } + public String getInviteeFirstName() { + return inviteeFirstName; + } + + public void setInviteeLastName(String inviteeLastName) { + this.inviteeLastName = inviteeLastName; + } + + public String getInviteeLastName() { + return inviteeLastName; + } + + public void setInviteeEmail(String inviteeEmail) { + this.inviteeEmail = inviteeEmail; + } + + public String getInviteeEmail() { + return inviteeEmail; + } + + public void setServerPath(String serverPath) { + this.serverPath = serverPath; + } + + public String getServerPath() { + return serverPath; + } + + public void setAcceptUrl(String acceptUrl) { + this.acceptUrl = acceptUrl; + } + + public String getAcceptUrl() { + return acceptUrl; + } + + public void setRejectUrl(String rejectUrl) { + this.rejectUrl = rejectUrl; + } + + public String getRejectUrl() { + return rejectUrl; + } + + public void setSentInviteDate(Date sentInviteDate) { + this.sentInviteDate = sentInviteDate; + } + + public Date getSentInviteDate() { + return sentInviteDate; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public String getTicket() { + return ticket; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRoleName() { + return roleName; + } + +} diff --git a/source/java/org/alfresco/repo/invitation/NominatedInvitationProcess.java b/source/java/org/alfresco/repo/invitation/NominatedInvitationProcess.java new file mode 100644 index 0000000000..0d9616ebef --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/NominatedInvitationProcess.java @@ -0,0 +1,61 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.service.cmr.invitation.Invitation; + +/** + * The Invitation process is the interface provided by the invitation service to be + * implemented by each resource handler + * + * This invitation process is where someone nominates an invitee who then needs to accept or + * reject the nomination. + */ + +public interface NominatedInvitationProcess extends InvitationProcess +{ + /* + * inviter starts the invitation process + */ + public Invitation invite(Invitation request, String comment); + + /** + * invitee accepts this request + * @param request + */ + public void accept(Invitation request); + + /** + * invitee rejects this request + * @param request + */ + public void reject(Invitation request); + + /** + * cancel this request + */ + public void cancel (Invitation request); + +} diff --git a/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java b/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java new file mode 100644 index 0000000000..dd31e4d4f5 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/WorkflowModelModeratedInvitation.java @@ -0,0 +1,73 @@ +package org.alfresco.repo.invitation; + +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +/** + * Workflow Model for a Moderated Invitation + */ +public interface WorkflowModelModeratedInvitation { + + // process name + public static final QName WF_PROCESS_INVITATION_MODERATED = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invitation-moderated"); + + // workflow definition name + public static final String WORKFLOW_DEFINITION_NAME = "jbpm$wf:invitation-moderated"; + + // tasks + public static final QName WF_START_TASK = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "moderatedInvitationSubmitTask"); + public static final QName WF_REVIEW_TASK = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI,"moderatedInvitationReviewTask"); + + // associations + static final QName ASSOC_GROUP_ASSIGNEE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "groupAssignee"); + + // transition names + public static final String WF_TRANSITION_REVIEW = "review"; + public static final String WF_TRANSITION_APPROVE = "approve"; + public static final String WF_TRANSITION_REJECT = "reject"; + public static final String WF_TRANSITION_CANCEL = "cancel"; + public static final String WF_TRANSITION_END = "end"; + + // workflow properties + public static final QName WF_PROP_INVITEE_USER_NAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeUserName"); + public static final QName WF_PROP_INVITEE_ROLE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeRole"); + public static final QName WF_PROP_INVITEE_COMMENTS = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeComments"); + public static final QName WF_PROP_RESOURCE_NAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "resourceName"); + public static final QName WF_PROP_RESOURCE_TYPE= QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "resourceType"); + public static final QName WF_PROP_REVIEW_COMMENTS= QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewComments"); + public static final QName WF_PROP_REVIEWER= QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewer"); + + // workflow execution context variable names + public static final String wfVarInviteeUserName = "wf_inviteeUserName"; + public static final String wfVarInviteeRole = "wf_inviteeRole"; + public static final String wfVarWorkflowInstanceId = "workflowinstanceid"; + public static final String wfVarResourceName = "wf_resourceName"; + public static final String wfVarResourceType = "wf_resourceType"; + public static final String wfVarReviewer = "wf_reviewer"; + public static final String wfVarReviewComments = "wf_reviewComments"; + } diff --git a/source/java/org/alfresco/repo/invitation/WorkflowModelNominatedInvitation.java b/source/java/org/alfresco/repo/invitation/WorkflowModelNominatedInvitation.java new file mode 100644 index 0000000000..3babc725c9 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/WorkflowModelNominatedInvitation.java @@ -0,0 +1,79 @@ +/* + * 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 recieved 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.invitation; + +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Workflow Model for a Nominated Invitation + */ +public interface WorkflowModelNominatedInvitation { + + // process name + public static final QName WF_PROCESS_INVITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invite"); + + // workflow definition name + public static final String WORKFLOW_DEFINITION_NAME = "jbpm$wf:invite"; + + // tasks + public static final QName WF_INVITE_TASK_INVITE_TO_SITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteToSiteTask"); + public static final QName WF_INVITE_TASK_INVITE_PENDING = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invitePendingTask"); + public static final QName WF_TASK_ACCEPT_INVITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "acceptInviteTask"); + public static final QName WF_TASK_REJECT_INVITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "rejectInviteTask"); + + // transition names + public static final String WF_TRANSITION_SEND_INVITE = "sendInvite"; + public static final String WF_TRANSITION_ACCEPT = "accept"; + public static final String WF_TRANSITION_REJECT = "reject"; + public static final String WF_TRANSITION_CANCEL = "cancel"; + public static final String WF_TRANSITION_ACCEPT_INVITE_END = "end"; + public static final String WF_TRANSITION_REJECT_INVITE_END = "end"; + + // workflow properties + public static final QName WF_PROP_SERVER_PATH = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "serverPath"); + public static final QName WF_PROP_ACCEPT_URL = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "acceptUrl"); + public static final QName WF_PROP_REJECT_URL = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "rejectUrl"); + public static final QName WF_PROP_INVITE_TICKET = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteTicket"); + public static final QName WF_PROP_INVITER_USER_NAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviterUserName"); + public static final QName WF_PROP_INVITEE_USER_NAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeUserName"); + public static final QName WF_PROP_INVITEE_FIRSTNAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeFirstName"); + public static final QName WF_PROP_INVITEE_LASTNAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeLastName"); + public static final QName WF_PROP_RESOURCE_TYPE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "resourceType"); + public static final QName WF_PROP_RESOURCE_NAME = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "resourceName"); + public static final QName WF_PROP_INVITEE_SITE_ROLE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeSiteRole"); + public static final QName WF_PROP_SENT_INVITE_DATE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "sentInviteDate"); + public static final QName WF_PROP_INVITEE_GEN_PASSWORD = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeGenPassword"); + + // workflow execution context variable names + public static final String wfVarInviteeUserName = "wf_inviteeUserName"; + public static final String wfVarInviterUserName = "wf_inviterUserName"; + public static final String wfVarResourceName = "wf_resourceName"; + public static final String wfVarResourceType = "wf_resourceType"; + public static final String wfVarWorkflowInstanceId = "workflowinstanceid"; + public static final String wfVarRole = "wf_inviteeSiteRole"; + +} diff --git a/source/java/org/alfresco/repo/invitation/package.html b/source/java/org/alfresco/repo/invitation/package.html new file mode 100644 index 0000000000..cf80f49fe9 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/package.html @@ -0,0 +1,6 @@ +The implementation of the Invitation Service. + +Which deals with invitations to private and moderated resources. + +@see org.alfresco.service.cmr.invitation.InvitationService + diff --git a/source/java/org/alfresco/repo/invitation/site/AcceptInviteAction.java b/source/java/org/alfresco/repo/invitation/site/AcceptInviteAction.java new file mode 100644 index 0000000000..418d4e1f73 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/AcceptInviteAction.java @@ -0,0 +1,95 @@ +/* + * 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.invitation.site; + +import org.alfresco.repo.invitation.WorkflowModelNominatedInvitation; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.site.SiteService; +import org.jbpm.graph.exe.ExecutionContext; +import org.springframework.beans.factory.BeanFactory; + +/** + * This class contains logic that gets executed when + * the wf:invitePendingTask in the invite workflow gets completed + * along the "accept" transition + * + * @author glen johnson at alfresco com + */ +public class AcceptInviteAction extends JBPMSpringActionHandler +{ + private static final long serialVersionUID = 8133039174866049136L; + + private SiteService siteService; + private MutableAuthenticationDao mutableAuthenticationDao; + + /* (non-Javadoc) + * @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory) + */ + @Override + protected void initialiseHandler(BeanFactory factory) + { + ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY); + mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao"); + siteService = services.getSiteService(); + } + + /* (non-Javadoc) + * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) + */ + @SuppressWarnings("unchecked") + public void execute(final ExecutionContext executionContext) throws Exception + { + final String inviteeUserName = (String) executionContext.getVariable(WorkflowModelNominatedInvitation.wfVarInviteeUserName); + final String siteShortName = (String) executionContext.getVariable(WorkflowModelNominatedInvitation.wfVarResourceName); + final String inviterUserName = (String) executionContext.getVariable(WorkflowModelNominatedInvitation.wfVarInviterUserName); + final String inviteeSiteRole = (String) executionContext.getVariable(WorkflowModelNominatedInvitation.wfVarRole); + + // if there is already a user account for the invitee and that account + // is disabled, then enable the account because he/she has accepted the + // site invitation + if ((this.mutableAuthenticationDao.userExists(inviteeUserName)) + && (this.mutableAuthenticationDao.getEnabled(inviteeUserName) == false)) + { + this.mutableAuthenticationDao.setEnabled(inviteeUserName, true); + } + + // add Invitee to Site with the site role that the inviter "started" the invite process with + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + AcceptInviteAction.this.siteService.setMembership(siteShortName, + inviteeUserName, inviteeSiteRole); + + return null; + } + + }, inviterUserName); + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java b/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java new file mode 100644 index 0000000000..d8da25cc82 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/CancelInviteAction.java @@ -0,0 +1,100 @@ +/* + * 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.invitation.site; + +import org.alfresco.repo.invitation.WorkflowModelNominatedInvitation; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.invitation.InvitationExceptionForbidden; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.jbpm.graph.exe.ExecutionContext; +import org.springframework.beans.factory.BeanFactory; + +/** + * This class contains logic that gets executed when + * the wf:invitePendingTask in the invite workflow gets cancelled + * along the "cancel" transition + * + * @author glen johnson at alfresco com + */ +public class CancelInviteAction extends JBPMSpringActionHandler +{ + private static final long serialVersionUID = 776961141883350908L; + + private MutableAuthenticationDao mutableAuthenticationDao; + private PersonService personService; + private WorkflowService workflowService; + private SiteService siteService; + + private final String MSG_NOT_SITE_MANAGER = "invitation.cancel.not_site_manager"; + + /* (non-Javadoc) + * @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory) + */ + @Override + protected void initialiseHandler(BeanFactory factory) + { + ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY); + mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao"); + personService = (PersonService) services.getPersonService(); + workflowService = (WorkflowService) services.getWorkflowService(); + siteService = (SiteService) services.getSiteService(); + } + + /* (non-Javadoc) + * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) + */ + @SuppressWarnings("unchecked") + public void execute(final ExecutionContext executionContext) throws Exception + { + // get the invitee user name and site short name variables off the execution context + final String inviteeUserName = (String) executionContext.getVariable( + WorkflowModelNominatedInvitation.wfVarInviteeUserName); + final String siteShortName = (String) executionContext.getVariable( + WorkflowModelNominatedInvitation.wfVarResourceName); + final String inviteId = (String) executionContext.getVariable( + WorkflowModelNominatedInvitation.wfVarWorkflowInstanceId); + + String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); + String currentUserSiteRole = this.siteService.getMembersRole(siteShortName, currentUserName); + if ((currentUserSiteRole == null) || (currentUserSiteRole.equals(SiteModel.SITE_MANAGER) == false)) + { + // The current user is not the site manager + Object[] args = {currentUserName, inviteId, siteShortName}; + throw new InvitationExceptionForbidden(MSG_NOT_SITE_MANAGER, args); + } + + // clean up invitee's user account and person node if they are not in use i.e. + // account is still disabled and there are no pending invites outstanding for the + // invitee + InviteHelper.cleanUpStaleInviteeResources(inviteeUserName, mutableAuthenticationDao, personService, + workflowService); + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/InviteHelper.java b/source/java/org/alfresco/repo/invitation/site/InviteHelper.java new file mode 100644 index 0000000000..47c2c8b4ca --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/InviteHelper.java @@ -0,0 +1,269 @@ +/* + * 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.invitation.site; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.invitation.WorkflowModelNominatedInvitation; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.template.TemplateNode; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +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.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskQuery; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Helper class to house utility methods common to + * more than one Invite Service Web Script + */ +public class InviteHelper +{ + /** + * Find an invite start task by the given task id. + * + * @return a WorkflowTask or null if not found. + */ + public static WorkflowTask findInviteStartTask(String inviteId, WorkflowService workflowService) + { + // create workflow task query + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + + wfTaskQuery.setProcessId(inviteId); + + // set process name to "wf:invite" so that only tasks associated with + // invite workflow instances are returned by query + wfTaskQuery.setProcessName(QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invite")); + + // filter to find only the invite start task + wfTaskQuery.setTaskState(WorkflowTaskState.COMPLETED); + wfTaskQuery.setTaskName(WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_TO_SITE); + + // query for invite workflow task associate + List inviteStartTasks = workflowService + .queryTasks(wfTaskQuery); + + // should also be 0 or 1 + if (inviteStartTasks.size() < 1) + { + return null; + } + else + { + return inviteStartTasks.get(0); + } + } + + /** + * Find invitePending tasks (in-progress) by the given invitee user name + * + * @return a list of workflow tasks + */ + public static List findInvitePendingTasks(String inviteeUserName, WorkflowService workflowService) + { + // create workflow task query + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + + // set process name to "wf:invite" so that only tasks associated with + // invite workflow instances are returned by query + wfTaskQuery.setProcessName(WorkflowModelNominatedInvitation.WF_PROCESS_INVITE); + + // set query to only pick up invite workflow instances + // associated with the given invitee user name + Map processCustomProps = new HashMap(1, 1.0f); + processCustomProps.put(WorkflowModelNominatedInvitation.WF_PROP_INVITEE_USER_NAME, inviteeUserName); + wfTaskQuery.setProcessCustomProps(processCustomProps); + + // set query to only pick up in-progress invite pending tasks + wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfTaskQuery.setTaskName(WorkflowModelNominatedInvitation.WF_INVITE_TASK_INVITE_PENDING); + + // query for invite workflow task associate + List inviteStartTasks = workflowService + .queryTasks(wfTaskQuery); + + return inviteStartTasks; + } + + /** + * Returns an InviteInfo instance for the given startInvite task + * (used for rendering the response). + * + * @param startInviteTask startInvite task to get invite info properties from + * @param serviceRegistry service registry instance + * @param siteService site service instance + * + * @return InviteInfo instance containing invite information + */ + public static InviteInfo getPendingInviteInfo(final WorkflowTask startInviteTask, + final ServiceRegistry serviceRegistry, final SiteService siteService) + { + final PersonService personService = serviceRegistry.getPersonService(); + + // get the inviter, invitee, role and site short name + final String inviterUserNameProp = (String) startInviteTask.properties.get( + WorkflowModelNominatedInvitation.WF_PROP_INVITER_USER_NAME); + final String inviteeUserNameProp = (String) startInviteTask.properties.get( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_USER_NAME); + final String role = (String) startInviteTask.properties.get( + WorkflowModelNominatedInvitation.WF_PROP_INVITEE_SITE_ROLE); + final String siteShortNameProp = (String) startInviteTask.properties.get( + WorkflowModelNominatedInvitation.WF_PROP_RESOURCE_NAME); + + // get the site info + SiteInfo siteInfo = siteService.getSite(siteShortNameProp); + + // get workflow instance id (associated with workflow task) to place + // as "inviteId" onto model + String workflowId = startInviteTask.path.instance.id; + + // set the invite start date to the time the workflow instance + // (associated with the task) was started + Date sentInviteDate = startInviteTask.path.instance.startDate; + + // TODO: glen johnson at alfresco com - as this web script only returns + // pending invites, this is hard coded to "pending" for now + String invitationStatus = InviteInfo.INVITATION_STATUS_PENDING; + + // fetch the person node for the inviter + NodeRef inviterRef = personService.getPerson(inviterUserNameProp); + TemplateNode inviterPerson = null; + if (inviterRef != null) + { + inviterPerson = new TemplateNode(inviterRef, serviceRegistry, null); + //inviterPerson = new ScriptNode(inviterRef, serviceRegistry); + } + + // fetch the person node for the invitee + NodeRef inviteeRef = personService.getPerson(inviteeUserNameProp); + TemplateNode inviteePerson = null; + if (inviteeRef != null) + { + inviteePerson = new TemplateNode(inviteeRef, serviceRegistry, null); + //inviteePerson = new ScriptNode(inviteeRef, serviceRegistry); + } + + // create and return a invite info + InviteInfo inviteInfo = new InviteInfo(invitationStatus, inviterUserNameProp, inviterPerson, + inviteeUserNameProp, inviteePerson, role, siteShortNameProp, siteInfo, sentInviteDate, workflowId); + + return inviteInfo; + } + + /** + * Clean up invitee user account and person node when no longer in use. + * They are deemed to no longer be in use when the invitee user account + * is still disabled and there are no outstanding pending invites for that invitee. + * + * @param inviteeUserName + * @param authenticationDao + * @param personService + * @param workflowService + */ + public static void cleanUpStaleInviteeResources(final String inviteeUserName, + final MutableAuthenticationDao authenticationDao, final PersonService personService, + final WorkflowService workflowService) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + // see if there are any pending invites (invite workflow instances with invitePending task in-progress) + // outstanding for given invitee user name + List pendingTasks = InviteHelper.findInvitePendingTasks(inviteeUserName, workflowService); + boolean invitesPending = (pendingTasks != null) && (pendingTasks.size() > 0); + + // if invitee's user account is still disabled and there are no pending invites outstanding + // for the invitee, then remove the account and delete the invitee's person node + if ((authenticationDao.userExists(inviteeUserName)) + && (authenticationDao.getEnabled(inviteeUserName) == false) + && (invitesPending == false)) + { + // delete the invitee's user account + authenticationDao.deleteUser(inviteeUserName); + + // delete the invitee's person node if one exists + if (personService.personExists(inviteeUserName)) + { + personService.deletePerson(inviteeUserName); + } + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Complete the specified Invite Workflow Task for the invite workflow + * instance associated with the given invite ID, and follow the given + * transition upon completing the task + * + * @param inviteId the invite ID of the invite workflow instance for which + * we want to complete the given task + * @param fullTaskName qualified name of invite workflow task to complete + * @param transitionId the task transition to take on completion of + * the task (or null, for the default transition) + */ + public static void completeInviteTask(String inviteId, QName fullTaskName, String transitionId, + final WorkflowService workflowService) + { + // create workflow task query + WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery(); + + // set the given invite ID as the workflow process ID in the workflow query + wfTaskQuery.setProcessId(inviteId); + + // find incomplete invite workflow tasks with given task name + wfTaskQuery.setActive(Boolean.TRUE); + wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS); + wfTaskQuery.setTaskName(fullTaskName); + + // set process name to "wf:invite" so that only + // invite workflow instances are considered by this query + wfTaskQuery.setProcessName(WorkflowModelNominatedInvitation.WF_PROCESS_INVITE); + + // query for invite workflow tasks with the constructed query + List wf_invite_tasks = workflowService + .queryTasks(wfTaskQuery); + + // end all tasks found with this name + for (WorkflowTask workflowTask : wf_invite_tasks) + { + workflowService.endTask(workflowTask.id, transitionId); + } + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/InviteInfo.java b/source/java/org/alfresco/repo/invitation/site/InviteInfo.java new file mode 100644 index 0000000000..86071cb395 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/InviteInfo.java @@ -0,0 +1,171 @@ +/* + * 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.invitation.site; + +import java.io.Serializable; +import java.util.Date; + +import org.alfresco.repo.template.TemplateNode; +import org.alfresco.service.cmr.site.SiteInfo; + +/** + * Holds properties pertaining to an invitation that has been sent out by a Site Manager (Inviter) + * to another person (Invitee) to join his/her Site + * + * @author glen dot johnson at alfresco dot com + */ +public class InviteInfo implements Serializable +{ + private static final long serialVersionUID = -4514253998906200208L; + + // invitation statuses + public static final String INVITATION_STATUS_PENDING = "pending"; + public static final String INVITATION_STATUS_ACCEPTED = "accepted"; + public static final String INVITATION_STATUS_REJECTED = "rejected"; + + // private instances to hold property values + private String invitationStatus; + private String inviterUserName; + private TemplateNode inviterPerson; + private String inviteeUserName; + private TemplateNode inviteePerson; + private String role; + private String siteShortName; + private SiteInfo siteInfo; + private Date sentInviteDate; + private String inviteId; + + public InviteInfo(String invitationStatus, String inviterUserName, TemplateNode inviterPerson, + String inviteeUserName, TemplateNode inviteePerson, String role, + String siteShortName, SiteInfo siteInfo, Date sentInviteDate, String inviteId) + { + this.invitationStatus = invitationStatus; + this.inviterUserName = inviterUserName; + this.inviterPerson = inviterPerson; + this.inviteeUserName = inviteeUserName; + this.inviteePerson = inviteePerson; + this.role = role; + this.siteShortName = siteShortName; + this.siteInfo = siteInfo; + this.sentInviteDate = sentInviteDate; + this.inviteId = inviteId; + } + + /** + * Gets the inviter user name + * + * @return the inviterUserName + */ + public String getInviterUserName() + { + return inviterUserName; + } + + /** + * Gets the invitee user name + * + * @return the inviteeUserName + */ + public String getInviteeUserName() + { + return inviteeUserName; + } + + /** + * Gets the site short name + * + * @return the siteShortName + */ + public String getSiteShortName() + { + return siteShortName; + } + + /** + * Gets the invite ID + * + * @return the inviteId + */ + public String getInviteId() + { + return inviteId; + } + + /** + * Gets the invitee person + * + * @return the invitee person + */ + public TemplateNode getInviteePerson() + { + return inviteePerson; + } + + /** + * Gets the inviter person + * + * @return the inviter person + */ + public TemplateNode getInviterPerson() + { + return inviterPerson; + } + + /** + * Gets the sent invite date + * + * @return the sent invite date + */ + public Date getSentInviteDate() + { + return sentInviteDate; + } + + /** + * Gets the invitation status + * + * @return the invitation status + */ + public String getInvitationStatus() + { + return invitationStatus; + } + + /** + * Gets the role that invitee has been invited to the site as + * + * @return the role that the invitee has been invited to the site as + */ + public String getRole() + { + return role; + } + + public SiteInfo getSiteInfo() + { + return siteInfo; + } + +} diff --git a/source/java/org/alfresco/repo/invitation/site/RejectInviteAction.java b/source/java/org/alfresco/repo/invitation/site/RejectInviteAction.java new file mode 100644 index 0000000000..4c5b2942f5 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/RejectInviteAction.java @@ -0,0 +1,78 @@ +/* + * 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.invitation.site; + +import org.alfresco.repo.invitation.WorkflowModelNominatedInvitation; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.jbpm.graph.exe.ExecutionContext; +import org.springframework.beans.factory.BeanFactory; + +/** + * This class contains logic that gets executed when + * the wf:invitePendingTask in the invite workflow gets completed + * along the "reject" transition + * + * @author glen johnson at alfresco com + */ +public class RejectInviteAction extends JBPMSpringActionHandler +{ + private static final long serialVersionUID = 4377660284993206875L; + + private MutableAuthenticationDao mutableAuthenticationDao; + private PersonService personService; + private WorkflowService workflowService; + + /* (non-Javadoc) + * @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory) + */ + @Override + protected void initialiseHandler(BeanFactory factory) + { + ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY); + mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao"); + personService = (PersonService) services.getPersonService(); + workflowService = (WorkflowService) services.getWorkflowService(); + } + + /* (non-Javadoc) + * @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext) + */ + @SuppressWarnings("unchecked") + public void execute(final ExecutionContext executionContext) throws Exception + { + // get the invitee user name + final String inviteeUserName = (String) executionContext.getVariable(WorkflowModelNominatedInvitation.wfVarInviteeUserName); + + // clean up invitee's user account and person node if they are not in use i.e. + // account is still disabled and there are no pending invites outstanding for the + // invitee + InviteHelper.cleanUpStaleInviteeResources(inviteeUserName, mutableAuthenticationDao, personService, + workflowService); + } +} diff --git a/source/java/org/alfresco/repo/invitation/site/SiteModeratedInvitationProcess.java b/source/java/org/alfresco/repo/invitation/site/SiteModeratedInvitationProcess.java new file mode 100644 index 0000000000..5e01881bea --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/SiteModeratedInvitationProcess.java @@ -0,0 +1,66 @@ +/* + * 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 recieved 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.invitation.site; + +import org.alfresco.repo.invitation.ModeratedInvitationProcess; +import org.alfresco.repo.invitation.NominatedInvitationProcess; +import org.alfresco.service.cmr.invitation.Invitation; + +/** + * The Site Private Invitation Process implements the PrivateInvitatonProcess for + * Web Sites. + */ +public class SiteModeratedInvitationProcess implements ModeratedInvitationProcess +{ + + public void approve(Invitation request, String reason) { + // TODO Auto-generated method stub + + } + + public void cancel(Invitation request, String reason) { + // TODO Auto-generated method stub + + } + + public void reject(Invitation request, String reason) { + // TODO Auto-generated method stub + + } + + public Invitation invite(Invitation request, String reason) { + // TODO Auto-generated method stub + return null; + + } + + public void cancel(Invitation request) { + // TODO Auto-generated method stub + + } + + + +} diff --git a/source/java/org/alfresco/repo/invitation/site/SiteNominatedInvitationProcess.java b/source/java/org/alfresco/repo/invitation/site/SiteNominatedInvitationProcess.java new file mode 100644 index 0000000000..3653f3f231 --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/SiteNominatedInvitationProcess.java @@ -0,0 +1,70 @@ +/* + * 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 recieved 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.invitation.site; + +import org.alfresco.repo.invitation.NominatedInvitationProcess; +import org.alfresco.service.cmr.invitation.Invitation; + +/** + * The Site Private Invitation Process implements the PrivateInvitatonProcess for + * Web Sites. + */ +public class SiteNominatedInvitationProcess implements NominatedInvitationProcess +{ + /** + * inviter starts the invitation process + */ + public Invitation invite(Invitation request, String reason) + { + return null; + } + + /** + * invitee accepts this request + * @param request + */ + public void accept(Invitation request) + { + + } + + /** + * invitee rejects this request + * @param request + */ + public void reject(Invitation request) + { + + } + + /** + * cancel this request + */ + public void cancel (Invitation request) + { + + } + +} diff --git a/source/java/org/alfresco/repo/invitation/site/package.html b/source/java/org/alfresco/repo/invitation/site/package.html new file mode 100644 index 0000000000..df802841ca --- /dev/null +++ b/source/java/org/alfresco/repo/invitation/site/package.html @@ -0,0 +1,4 @@ +The implementation of the Invitation Service for Web Site resources. + +@see org.alfresco.service.cmr.invitation.InvitationService + diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index ceedaa2bf1..f7e4e46c4c 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -28,7 +28,6 @@ import java.util.Collection; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.forms.FormService; -import org.alfresco.repo.site.SiteService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; @@ -40,6 +39,7 @@ import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.invitation.InvitationService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.EditionService; @@ -60,6 +60,7 @@ import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.version.VersionService; @@ -496,4 +497,13 @@ public class ServiceDescriptorRegistry { return (FormService)getService(FORM_SERVICE); } + + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getInvitationService() + */ + public InvitationService getInvitationService() + { + return (InvitationService)getService(INVITATION_SERVICE); + + } } diff --git a/source/java/org/alfresco/repo/site/SiteInfo.java b/source/java/org/alfresco/repo/site/SiteInfoImpl.java similarity index 55% rename from source/java/org/alfresco/repo/site/SiteInfo.java rename to source/java/org/alfresco/repo/site/SiteInfoImpl.java index 7b3a31ce31..35880d057e 100644 --- a/source/java/org/alfresco/repo/site/SiteInfo.java +++ b/source/java/org/alfresco/repo/site/SiteInfoImpl.java @@ -29,6 +29,8 @@ import java.util.HashMap; import java.util.Map; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.namespace.QName; /** @@ -36,7 +38,7 @@ import org.alfresco.service.namespace.QName; * * @author Roy Wetherall */ -public class SiteInfo +public class SiteInfoImpl implements SiteInfo { /** Site node reference */ private NodeRef nodeRef; @@ -53,8 +55,8 @@ public class SiteInfo /** Site description */ private String description; - /** Indicates whether the site is public or not */ - private boolean isPublic; + /** Site visibility */ + private SiteVisibility visibility; /** Set of custom properties that have been defined for site */ private Map customProperties = new HashMap(1); @@ -66,12 +68,12 @@ public class SiteInfo * @param shortName short name * @param title title * @param description description - * @param isPublic is site public + * @param visibility site visibility * @param nodeRef site node reference */ - /*package*/ SiteInfo(String sitePreset, String shortName, String title, String description, boolean isPublic, Map customProperties, NodeRef nodeRef) + /*package*/ SiteInfoImpl(String sitePreset, String shortName, String title, String description, SiteVisibility visibility, Map customProperties, NodeRef nodeRef) { - this(sitePreset, shortName, title, description, isPublic, customProperties); + this(sitePreset, shortName, title, description, visibility, customProperties); this.nodeRef = nodeRef; } @@ -82,15 +84,15 @@ public class SiteInfo * @param shortName short name * @param title title * @param description description - * @param isPublic is site public + * @param visibility site visibility */ - /*package*/ SiteInfo(String sitePreset, String shortName, String title, String description, boolean isPublic, Map customProperties) + /*package*/ SiteInfoImpl(String sitePreset, String shortName, String title, String description, SiteVisibility visibility, Map customProperties) { this.sitePreset = sitePreset; this.shortName = shortName; this.title = title; this.description = description; - this.isPublic = isPublic; + this.visibility = visibility; if (customProperties != null) { this.customProperties = customProperties; @@ -98,9 +100,7 @@ public class SiteInfo } /** - * Get the site node reference - * - * @return NodeRef site node reference, null if not set + * @see org.alfresco.repo.site.SiteInfo#getNodeRef() */ public NodeRef getNodeRef() { @@ -108,9 +108,7 @@ public class SiteInfo } /** - * Get the site preset - * - * @return String site preset + * @see org.alfresco.repo.site.SiteInfo#getSitePreset() */ public String getSitePreset() { @@ -118,9 +116,7 @@ public class SiteInfo } /** - * Get the short name - * - * @return String short name + * @see org.alfresco.repo.site.SiteInfo#getShortName() */ public String getShortName() { @@ -128,9 +124,7 @@ public class SiteInfo } /** - * Get the title - * - * @return String site title + * @see org.alfresco.repo.site.SiteInfo#getTitle() */ public String getTitle() { @@ -138,9 +132,7 @@ public class SiteInfo } /** - * Set the title - * - * @param title site title + * @see org.alfresco.repo.site.SiteInfo#setTitle(java.lang.String) */ public void setTitle(String title) { @@ -148,9 +140,7 @@ public class SiteInfo } /** - * Get the description - * - * @return String site description + * @see org.alfresco.repo.site.SiteInfo#getDescription() */ public String getDescription() { @@ -158,9 +148,7 @@ public class SiteInfo } /** - * Set the description - * - * @param description site description + * @see org.alfresco.repo.site.SiteInfo#setDescription(java.lang.String) */ public void setDescription(String description) { @@ -168,29 +156,51 @@ public class SiteInfo } /** - * Sets whether the site is public or not - * - * @param isPublic true if the site is public, false otherwise + * @see org.alfresco.repo.site.SiteInfo#setIsPublic(boolean) */ public void setIsPublic(boolean isPublic) { - this.isPublic = isPublic; + if (isPublic == true) + { + setVisibility(SiteVisibility.PUBLIC); + } + else + { + setVisibility(SiteVisibility.PRIVATE); + } } /** - * Indicates wehther the site is public - * - * @return boolean true if public false otherwise + * @see org.alfresco.repo.site.SiteInfo#getIsPublic() */ public boolean getIsPublic() { - return this.isPublic; + boolean result = false; + if (SiteVisibility.PUBLIC.equals(this.visibility) == true) + { + result = true; + } + return result; } /** - * Get the custom property values - * - * @return Map map of custom property names and values + * @see org.alfresco.service.cmr.site.SiteInfo#getVisibility() + */ + public SiteVisibility getVisibility() + { + return this.visibility; + } + + /** + * @see org.alfresco.service.cmr.site.SiteInfo#setVisibility(org.alfresco.service.cmr.site.SiteVisibility) + */ + public void setVisibility(SiteVisibility visibility) + { + this.visibility = visibility; + } + + /** + * @see org.alfresco.repo.site.SiteInfo#getCustomProperties() */ public Map getCustomProperties() { @@ -198,10 +208,7 @@ public class SiteInfo } /** - * Get the value of a custom property - * - * @param name name of custom property - * @return Serializable value of the property, null if not set or doesn't exist + * @see org.alfresco.repo.site.SiteInfo#getCustomProperty(org.alfresco.service.namespace.QName) */ public Serializable getCustomProperty(QName name) { @@ -211,5 +218,35 @@ public class SiteInfo result = this.customProperties.get(name); } return result; + } + + /** + * Override equals for this ref type + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj instanceof SiteInfoImpl) + { + SiteInfoImpl that = (SiteInfoImpl) obj; + return (this.shortName.equals(that.shortName)); + } + else + { + return false; + } + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return this.shortName.hashCode(); } } diff --git a/source/java/org/alfresco/repo/site/SiteModel.java b/source/java/org/alfresco/repo/site/SiteModel.java index 469fee53f0..e1be6d5aaf 100644 --- a/source/java/org/alfresco/repo/site/SiteModel.java +++ b/source/java/org/alfresco/repo/site/SiteModel.java @@ -43,6 +43,7 @@ public interface SiteModel public static final QName TYPE_SITES = QName.createQName(SITE_MODEL_URL, "sites"); public static final QName TYPE_SITE = QName.createQName(SITE_MODEL_URL, "site"); public static final QName PROP_SITE_PRESET = QName.createQName(SITE_MODEL_URL, "sitePreset"); + public static final QName PROP_SITE_VISIBILITY = QName.createQName(SITE_MODEL_URL, "siteVisibility"); /** Site Container */ public static final QName ASPECT_SITE_CONTAINER = QName.createQName(SITE_MODEL_URL, "siteContainer"); diff --git a/source/java/org/alfresco/repo/site/SiteServiceException.java b/source/java/org/alfresco/repo/site/SiteServiceException.java new file mode 100644 index 0000000000..6709170ff6 --- /dev/null +++ b/source/java/org/alfresco/repo/site/SiteServiceException.java @@ -0,0 +1,61 @@ +/** + * + */ +package org.alfresco.repo.site; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Site service exception + * + * @author Roy Wetherall + */ +public class SiteServiceException extends AlfrescoRuntimeException +{ + /** Serial version UID */ + private static final long serialVersionUID = -5838634544722182609L; + + /** + * Constructor + * + * @param msgId message id + */ + public SiteServiceException(String msgId) + { + super(msgId); + } + + /** + * Constructor + * + * @param msgId message id + * @param msgParams message params + */ + public SiteServiceException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + /** + * Constructor + * + * @param msgId message id + * @param cause causing exception + */ + public SiteServiceException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + /** + * Constructor + * + * @param msgId message id + * @param msgParams message params + * @param cause causing exception + */ + public SiteServiceException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index a0b9405146..f45d874f6f 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.activities.ActivityType; import org.alfresco.repo.security.authentication.AuthenticationComponent; @@ -52,10 +51,14 @@ import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; @@ -76,10 +79,21 @@ public class SiteServiceImpl implements SiteService, SiteModel /** The DM store where site's are kept */ public static final StoreRef SITE_STORE = new StoreRef("workspace://SpacesStore"); - /** Activiti tool */ + /** Activity tool */ private static final String ACTIVITY_TOOL = "siteService"; private String sitesXPath; + + /** Messages */ + private static final String MSG_UNABLE_TO_CREATE = "site_service.unable_to_create"; + private static final String MSG_CAN_NOT_UPDATE = "site_service.can_not_update"; + private static final String MSG_CAN_NOT_DELETE = "site_service.can_not_delete"; + private static final String MSG_SITE_NO_EXIST = "site_service.site_no_exist"; + private static final String MSG_DO_NOT_REMOVE_MGR = "site_service.do_not_remove_manager"; + private static final String MSG_CAN_NOT_REMOVE_MSHIP = "site_service.can_not_reomve_memebership"; + private static final String MSG_DO_NOT_CHANGE_MGR = "site_service.do_not_change_manager"; + private static final String MSG_CAN_NOT_CHANGE_MSHIP="site_service.can_not_change_memebership"; + private static final String MSG_SITE_CONTAINER_NOT_FOLDER = "site_service.site_container_not_folder"; /* Services */ private NodeService nodeService; @@ -172,7 +186,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * Set the taggin service + * Set the tagging service */ public void setTaggingService(TaggingService taggingService) { @@ -214,12 +228,35 @@ public class SiteServiceImpl implements SiteService, SiteModel PropertyCheck.mandatory(this, "authorityService", authorityService); PropertyCheck.mandatory(this, "sitesXPath", sitesXPath); } + + /** + * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) + */ + public SiteInfo createSite( final String sitePreset, + String passedShortName, + final String title, + final String description, + final boolean isPublic) + { + // Determine the site visibility + SiteVisibility visibility = SiteVisibility.PRIVATE; + if (isPublic == true) + { + visibility = SiteVisibility.PUBLIC; + } + + // Create the site + return createSite(sitePreset, passedShortName, title, description, visibility); + } /** - * {@inheritDoc} + * @see org.alfresco.service.cmr.site.SiteService#createSite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ - public SiteInfo createSite(final String sitePreset, String passedShortName, - final String title, final String description, final boolean isPublic) + public SiteInfo createSite(final String sitePreset, + String passedShortName, + final String title, + final String description, + final SiteVisibility visibility) { // Remove spaces from shortName final String shortName = passedShortName.replaceAll(" ", ""); @@ -229,10 +266,7 @@ public class SiteServiceImpl implements SiteService, SiteModel if (existingSite != null) { // Throw an exception since we have a duplicate site name - throw new AlfrescoRuntimeException( - "Unable to create site because the site short name '" - + shortName - + "' is already in use. Site short names must be unique."); + throw new SiteServiceException(MSG_UNABLE_TO_CREATE, new Object[]{shortName}); } // Get the site parent node reference @@ -242,6 +276,7 @@ public class SiteServiceImpl implements SiteService, SiteModel PropertyMap properties = new PropertyMap(4); properties.put(ContentModel.PROP_NAME, shortName); properties.put(SiteModel.PROP_SITE_PRESET, sitePreset); + properties.put(SiteModel.PROP_SITE_VISIBILITY, visibility.toString()); properties.put(ContentModel.PROP_TITLE, title); properties.put(ContentModel.PROP_DESCRIPTION, description); @@ -270,8 +305,7 @@ public class SiteServiceImpl implements SiteService, SiteModel String siteGroup = authorityService.createAuthority( AuthorityType.GROUP, null, getSiteGroup(shortName, false)); - Set permissions = permissionService - .getSettablePermissions(SiteModel.TYPE_SITE); + Set permissions = permissionService.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { // Create a group for the permission @@ -280,20 +314,22 @@ public class SiteServiceImpl implements SiteService, SiteModel shortName, permission, false)); // Assign the group the relevant permission on the site - permissionService.setPermission(siteNodeRef, - permissionGroup, permission, true); + permissionService.setPermission(siteNodeRef, permissionGroup, permission, true); } - // Set the memberhips details - // - give all authorities read permissions if site is public + // Set the memberships details + // - give all authorities site consumer if site is public + // - give all authorities read properties if site is moderated // - give all authorities read permission on permissions so // memberships can be calculated // - add the current user to the site manager group - if (isPublic == true) + if (SiteVisibility.PUBLIC.equals(visibility) == true) { - permissionService.setPermission(siteNodeRef, - PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, - true); + permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); + } + else if (SiteVisibility.MODERATED.equals(visibility) == true) + { + permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ_PROPERTIES, true); } permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, @@ -309,7 +345,7 @@ public class SiteServiceImpl implements SiteService, SiteModel // Return created site information Map customProperties = getSiteCustomProperties(siteNodeRef); - SiteInfo siteInfo = new SiteInfo(sitePreset, shortName, title, description, isPublic, customProperties, siteNodeRef); + SiteInfo siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); return siteInfo; } @@ -335,7 +371,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#getSiteGroup(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#getSiteGroup(java.lang.String) */ public String getSiteGroup(String shortName) { @@ -343,7 +379,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#getSiteRoleGroup(java.lang.String, + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoleGroup(java.lang.String, * java.lang.String) */ public String getSiteRoleGroup(String shortName, String role) @@ -420,7 +456,7 @@ public class SiteServiceImpl implements SiteService, SiteModel if (results.size() == 0) { // No root site folder exists - throw new AlfrescoRuntimeException("No root sites folder exists"); + throw new SiteServiceException("No root sites folder exists"); } else if (results.size() != 1) { @@ -432,7 +468,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#listSites(java.lang.String, + * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String, * java.lang.String) */ public List listSites(String nameFilter, String sitePresetFilter) @@ -463,7 +499,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#listSites(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#listSites(java.lang.String) */ public List listSites(String userName) { @@ -480,7 +516,7 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * Creates a site informatoin object given a site node reference + * Creates a site information object given a site node reference * * @param siteNodeRef * site node reference @@ -497,42 +533,59 @@ public class SiteServiceImpl implements SiteService, SiteModel String description = (String) properties .get(ContentModel.PROP_DESCRIPTION); - // Determine whether the space is public or not - boolean isPublic = isSitePublic(siteNodeRef); - + // Get the visibility of the site + SiteVisibility visibility = getSiteVisibility(siteNodeRef); + // Create and return the site information Map customProperties = getSiteCustomProperties(siteNodeRef); - SiteInfo siteInfo = new SiteInfo(sitePreset, shortName, title, description, isPublic, customProperties, siteNodeRef); + SiteInfo siteInfo = new SiteInfoImpl(sitePreset, shortName, title, description, visibility, customProperties, siteNodeRef); return siteInfo; } - + /** - * Indicates whether a site is public or not + * Helper method to get the visibility of the site. If no value is present in the repository then it is calculated from the + * set permissions. This will maintain backwards compatibility with earlier versions of the service implementation. * - * @param siteNodeRef - * site node reference - * @return boolean true if the site is public, false otherwise + * @param siteNodeRef site node reference + * @return SiteVisibility site visibility */ - private boolean isSitePublic(NodeRef siteNodeRef) + private SiteVisibility getSiteVisibility(NodeRef siteNodeRef) { - boolean isPublic = false; - Set permissions = this.permissionService - .getAllSetPermissions(siteNodeRef); - for (AccessPermission permission : permissions) + SiteVisibility visibility = SiteVisibility.PRIVATE; + + // Get the visibility value stored in the repo + String visibilityValue = (String)this.nodeService.getProperty(siteNodeRef, SiteModel.PROP_SITE_VISIBILITY); + + // To maintain backwards compatibility calculate the visibility from the permissions + // if there is no value specified on the site node + if (visibilityValue == null) { - if (permission.getAuthority().equals( - PermissionService.ALL_AUTHORITIES) == true - && permission.getPermission().equals(SITE_CONSUMER) == true) + // Examine each permission to see if this is a public site or not + Set permissions = this.permissionService.getAllSetPermissions(siteNodeRef); + for (AccessPermission permission : permissions) { - isPublic = true; - break; + if (permission.getAuthority().equals(PermissionService.ALL_AUTHORITIES) == true && + permission.getPermission().equals(SITE_CONSUMER) == true) + { + visibility = SiteVisibility.PUBLIC; + break; + } } + + // Store the visibility value on the node ref for next time + this.nodeService.setProperty(siteNodeRef, SiteModel.PROP_SITE_VISIBILITY, visibility.toString()); } - return isPublic; + else + { + // Create the enum value from the string + visibility = SiteVisibility.valueOf(visibilityValue); + } + + return visibility; } /** - * @see org.alfresco.repo.site.SiteService#getSite(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#getSite(java.lang.String) */ public SiteInfo getSite(String shortName) { @@ -572,62 +625,74 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#updateSite(org.alfresco.repo.site.SiteInfo) + * @see org.alfresco.service.cmr.site.SiteService#updateSite(org.alfresco.service.cmr.site.SiteInfo) */ public void updateSite(SiteInfo siteInfo) { NodeRef siteNodeRef = getSiteNodeRef(siteInfo.getShortName()); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Can not update site " - + siteInfo.getShortName() + " because it does not exist."); + throw new SiteServiceException(MSG_CAN_NOT_UPDATE, new Object[]{siteInfo.getShortName()}); } - - // Note: the site preset and short name can not be updated - + + // Get the sites properties + Map properties = this.nodeService.getProperties(siteNodeRef); + // Update the properties of the site - Map properties = this.nodeService - .getProperties(siteNodeRef); + // Note: the site preset and short name can not be updated properties.put(ContentModel.PROP_TITLE, siteInfo.getTitle()); - properties - .put(ContentModel.PROP_DESCRIPTION, siteInfo.getDescription()); - this.nodeService.setProperties(siteNodeRef, properties); + properties.put(ContentModel.PROP_DESCRIPTION, siteInfo.getDescription()); // Update the isPublic flag - boolean isPublic = isSitePublic(siteNodeRef); - if (isPublic != siteInfo.getIsPublic()) - ; + SiteVisibility currentVisibility = getSiteVisibility(siteNodeRef); + SiteVisibility updatedVisibility = siteInfo.getVisibility(); + if (currentVisibility.equals(updatedVisibility) == false) { - if (siteInfo.getIsPublic() == true) + // Remove current visibility permissions + if (SiteVisibility.PUBLIC.equals(currentVisibility) == true) { - // Add the permission - this.permissionService.setPermission(siteNodeRef, - PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); - } else - { - // Remove the permission - this.permissionService.deletePermission(siteNodeRef, - PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); + this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER); } + else if (SiteVisibility.MODERATED.equals(currentVisibility) == true) + { + this.permissionService.deletePermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ_PROPERTIES); + // TODO update all child folders ?? ... + } + + // Add new visibility permissions + if (SiteVisibility.PUBLIC.equals(updatedVisibility) == true) + { + this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true); + } + else if (SiteVisibility.MODERATED.equals(updatedVisibility) == true) + { + this.permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ_PROPERTIES, true); + // TODO update all child folders ?? ... + } + + // Update the site node reference with the updated visibility value + properties.put(SiteModel.PROP_SITE_VISIBILITY, siteInfo.getVisibility()); } + + // Set the updated properties back onto the site node reference + this.nodeService.setProperties(siteNodeRef, properties); } - + /** - * @see org.alfresco.repo.site.SiteService#deleteSite(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#deleteSite(java.lang.String) */ public void deleteSite(final String shortName) { NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Can not delete site " - + shortName + " because it does not exist."); + throw new SiteServiceException(MSG_CAN_NOT_DELETE, new Object[]{shortName}); } // Delete the node this.nodeService.deleteNode(siteNodeRef); - // Delete the associatated group's + // Delete the associated group's AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { public Object doWork() throws Exception @@ -639,96 +704,166 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#listMembers(java.lang.String, - * java.lang.String, java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#listMembers(java.lang.String, java.lang.String, java.lang.String) */ - public Map listMembers(String shortName, String nameFilter, - String roleFilter) + public Map listMembers(String shortName, String nameFilter, String roleFilter) + { + return listMembers(shortName, nameFilter, roleFilter, false); + } + + /** + * @see org.alfresco.service.cmr.site.SiteService#listMembers(String, String, String, boolean) + */ + public Map listMembers(String shortName, String nameFilter, String roleFilter, boolean collapseGroups) { NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } Map members = new HashMap(23); - Set permissions = permissionService - .getSettablePermissions(SiteModel.TYPE_SITE); + Set permissions = permissionService.getSettablePermissions(SiteModel.TYPE_SITE); for (String permission : permissions) { - if (roleFilter == null || - roleFilter.length() == 0 || - roleFilter.equals(permission) == true) + if (filterMatch(roleFilter, permission) == true) { String groupName = getSiteRoleGroup(shortName, permission, true); - Set users = this.authorityService.getContainedAuthorities( - AuthorityType.USER, groupName, true); + Set users = this.authorityService.getContainedAuthorities(AuthorityType.USER, groupName, true); for (String user : users) { - if (nameFilter == null || - nameFilter.length() == 0 || - nameFilter.equals(user) == true) + if (filterMatch(nameFilter, user) == true) { // Add the user and their permission to the returned map members.put(user, permission); } } + Set groups = this.authorityService.getContainedAuthorities(AuthorityType.GROUP, groupName, true); + for (String group : groups) + { + if (collapseGroups == false) + { + if (filterMatch(nameFilter, group) == true) + { + // Add the group and their permission to the returned map + members.put(group, permission); + } + } + else + { + Set subUsers = this.authorityService.getContainedAuthorities(AuthorityType.USER, group, false); + for (String subUser : subUsers) + { + if (filterMatch(nameFilter, subUser) == true) + { + // Add the collapsed user into the members list if they do not already appear in the list + if (members.containsKey(subUser) == false) + { + members.put(subUser, permission); + } + } + } + } + + } } } return members; } + + /** + * Helper method to calculate whether a value matches a filter or not + * + * @param filter filter + * @param value value + * @return boolean true if the value matches the filter, false otherwise + */ + private boolean filterMatch(String filter, String value) + { + boolean result = false; + if (filter == null || + filter.length() == 0 || + filter.equals(value) == true) + { + result = true; + } + return result; + } /** - * @see org.alfresco.repo.site.SiteService#getMembersRole(java.lang.String, + * @see org.alfresco.service.cmr.site.SiteService#getMembersRole(java.lang.String, * java.lang.String) */ - public String getMembersRole(String shortName, String userName) + public String getMembersRole(String shortName, String authorityName) { String result = null; - String group = getPermissionGroup(shortName, userName); - if (group != null) + List roles = getMembersRoles(shortName, authorityName); + if (roles.isEmpty() == false) + { + result = roles.get(0); + } + return result; + } + + public List getMembersRoles(String shortName, String authorityName) + { + List result = new ArrayList(5); + List groups = getPermissionGroups(shortName, authorityName); + for (String group : groups) { int index = group.lastIndexOf('_'); if (index != -1) { - result = group.substring(index + 1); + result.add(group.substring(index + 1)); } } return result; } - + + private List getPermissionGroups(String siteShortName, String authorityName) + { + List result = new ArrayList(5); + Set roles = permissionService.getSettablePermissions(SiteModel.TYPE_SITE); + for (String role : roles) + { + String roleGroup = getSiteRoleGroup(siteShortName, role, true); + Set authorities = this.authorityService.getContainedAuthorities(null, roleGroup, false); + if (authorities.contains(authorityName) == true) + { + result.add(roleGroup); + } + } + return result; + } + /** - * Helper method to get the permission group for a given user on a site. + * Helper method to get the permission group for a given authority on a site. * Returns null if the user does not have a explicit membership to the site. * - * @param siteShortName - * site short name - * @param userName - * user name - * @return String permission group, null if no explicit membership set + * @param siteShortName site short name + * @param authorityName authority name + * @return String permission group, null if no explicit membership set */ - private String getPermissionGroup(String siteShortName, String userName) - { - String result = null; - Set groups = this.authorityService.getContainingAuthorities( - AuthorityType.GROUP, userName, true); - for (String group : groups) - { - if (group.startsWith(PermissionService.GROUP_PREFIX + "site_" - + siteShortName) == true) - { - result = group; - break; - } - } - return result; - } +// private String getPermissionGroup(String siteShortName, String authorityName) +// { +// String result = null; +// Set groups = this.authorityService.getContainingAuthorities(AuthorityType.GROUP, authorityName, true); +// for (String group : groups) +// { +// if (group.startsWith(PermissionService.GROUP_PREFIX + "site_" +// + siteShortName) == true) +// { +// result = group; +// break; +// } +// } +// return result; +// } /** - * @see org.alfresco.repo.site.SiteService#getSiteRoles() + * @see org.alfresco.service.cmr.site.SiteService#getSiteRoles() */ public List getSiteRoles() { @@ -738,38 +873,32 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#isMember(java.lang.String, - * java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#isMember(java.lang.String, java.lang.String) */ - public boolean isMember(String shortName, String userName) + public boolean isMember(String shortName, String authorityName) { - return (getPermissionGroup(shortName, userName) != null); + return (!getPermissionGroups(shortName, authorityName).isEmpty()); } /** - * @see org.alfresco.repo.site.SiteService#removeMembership(java.lang.String, - * java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#removeMembership(java.lang.String, java.lang.String) */ - public void removeMembership(final String shortName, final String userName) + public void removeMembership(final String shortName, final String authorityName) { final NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } // TODO what do we do about the user if they are in a group that has // rights to the site? - // Determine whether the site is private or not - boolean isPublic = isSitePublic(siteNodeRef); - // Get the current user String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); // Get the user current role - final String role = getMembersRole(shortName, userName); + final String role = getMembersRole(shortName, authorityName); if (role != null) { // Check that we are not about to remove the last site manager @@ -782,10 +911,7 @@ public class SiteServiceImpl implements SiteService, SiteModel true); if (siteMangers.size() == 1) { - throw new AlfrescoRuntimeException( - "A site requires at least one site manager. You can not remove '" - + userName - + "' from the site memebership because they are currently the only site manager."); + throw new SiteServiceException(MSG_DO_NOT_REMOVE_MGR, new Object[]{authorityName}); } } @@ -793,7 +919,7 @@ public class SiteServiceImpl implements SiteService, SiteModel // -- the current user has change permissions rights on the site // or // -- the user is ourselves - if ((currentUserName.equals(userName) == true) || + if ((currentUserName.equals(authorityName) == true) || (permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)) { // Run as system user @@ -805,187 +931,172 @@ public class SiteServiceImpl implements SiteService, SiteModel // Remove the user from the current permission // group String currentGroup = getSiteRoleGroup(shortName, role, true); - authorityService.removeAuthority(currentGroup, userName); + authorityService.removeAuthority(currentGroup, authorityName); return null; } }, AuthenticationUtil.SYSTEM_USER_NAME); // Raise events - if (AuthorityType.getAuthorityType(userName) == AuthorityType.USER) + if (AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER) { activityService.postActivity( ActivityType.SITE_USER_REMOVED, shortName, - ACTIVITY_TOOL, getActivityData(userName, "")); + ACTIVITY_TOOL, getActivityData(authorityName, "")); } else { // TODO - update this, if sites support groups logger.error("setMembership - failed to post activity: unexpected authority type: " - + AuthorityType.getAuthorityType(userName)); + + AuthorityType.getAuthorityType(authorityName)); } } else { - // Throw a permission exception - throw new AlfrescoRuntimeException( - "Access denied, user does not have permissions to delete membership details of the site '" - + shortName + "'"); + // Throw an exception + throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); } - } else + } + else { - // Throw a permission exception - throw new AlfrescoRuntimeException( - "Access denied, user does not have permissions to delete membership details of the site '" - + shortName + "'"); + // Throw an exception + throw new SiteServiceException(MSG_CAN_NOT_REMOVE_MSHIP, new Object[]{shortName}); } } /** - * @see org.alfresco.repo.site.SiteService#setMembership(java.lang.String, + * @see org.alfresco.service.cmr.site.SiteService#setMembership(java.lang.String, * java.lang.String, java.lang.String) */ - public void setMembership(final String shortName, final String userName, - final String role) + public void setMembership(final String shortName, + final String authorityName, + final String role) { final NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } // Get the user's current role - final String currentRole = getMembersRole(shortName, userName); + final String currentRole = getMembersRole(shortName, authorityName); // Do nothing if the role of the user is not being changed if (currentRole == null || role.equals(currentRole) == false) { - // Determine whether the site is private or not - boolean isPublic = isSitePublic(siteNodeRef); - - // TODO if this is the only site manager do not downgrade their + // TODO if this is the only site manager do not down grade their // permissions + + // Get the visibility of the site + SiteVisibility visibility = getSiteVisibility(siteNodeRef); // If we are ... // -- the current user has change permissions rights on the site // or we are ... - // -- refering to a public site and + // -- referring to a public site and // -- the role being set is consumer and // -- the user being added is ourselves and // -- the member does not already have permissions // ... then we can set the permissions as system user final String currentUserName = AuthenticationUtil.getFullyAuthenticatedUser(); - if ((permissionService.hasPermission(siteNodeRef, - PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) - || (isPublic == true - && role.equals(SiteModel.SITE_CONSUMER) == true - && userName.equals(currentUserName) == true && currentRole == null)) + if ((permissionService.hasPermission(siteNodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) || + (SiteVisibility.PUBLIC.equals(visibility) == true && + role.equals(SiteModel.SITE_CONSUMER) == true && + authorityName.equals(currentUserName) == true && + currentRole == null)) { // Check that we are not about to remove the last site manager if (SiteModel.SITE_MANAGER.equals(currentRole) == true) { - Set siteMangers = this.authorityService - .getContainedAuthorities(AuthorityType.USER, - getSiteRoleGroup(shortName, SITE_MANAGER, - true), true); + Set siteMangers = this.authorityService.getContainedAuthorities(AuthorityType.USER, + getSiteRoleGroup(shortName, SITE_MANAGER, true), + true); if (siteMangers.size() == 1) { - throw new AlfrescoRuntimeException( - "A site requires at least one site manager. You can not change '" - + userName - + "' role from the site memebership because they are currently the only site manager."); + throw new SiteServiceException(MSG_DO_NOT_CHANGE_MGR, new Object[]{authorityName}); } } // Run as system user - AuthenticationUtil.runAs( - new AuthenticationUtil.RunAsWork() + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + if (currentRole != null) { - public Object doWork() throws Exception - { - if (currentRole != null) - { - // Remove the user from the current - // permission group - String currentGroup = getSiteRoleGroup( - shortName, currentRole, true); - authorityService.removeAuthority( - currentGroup, userName); - } + // Remove the user from the current + // permission group + String currentGroup = getSiteRoleGroup(shortName, currentRole, true); + authorityService.removeAuthority(currentGroup, authorityName); + } - // Add the user to the new permission group - String newGroup = getSiteRoleGroup(shortName, - role, true); - authorityService.addAuthority(newGroup, - userName); + // Add the user to the new permission group + String newGroup = getSiteRoleGroup(shortName, role, true); + authorityService.addAuthority(newGroup, authorityName); - return null; - } + return null; + } - }, AuthenticationUtil.SYSTEM_USER_NAME); + }, AuthenticationUtil.SYSTEM_USER_NAME); if (currentRole == null) { - if (AuthorityType.getAuthorityType(userName) == AuthorityType.USER) + if (AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER) { activityService.postActivity( ActivityType.SITE_USER_JOINED, shortName, - ACTIVITY_TOOL, getActivityData(userName, role)); - } else + ACTIVITY_TOOL, getActivityData(authorityName, role)); + } + else { // TODO - update this, if sites support groups logger .error("setMembership - failed to post activity: unexpected authority type: " - + AuthorityType - .getAuthorityType(userName)); + + AuthorityType.getAuthorityType(authorityName)); } - } else + } + else { - if (AuthorityType.getAuthorityType(userName) == AuthorityType.USER) + if (AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER) { activityService.postActivity( ActivityType.SITE_USER_ROLE_UPDATE, shortName, - ACTIVITY_TOOL, getActivityData(userName, role)); - } else + ACTIVITY_TOOL, getActivityData(authorityName, role)); + } + else { // TODO - update this, if sites support groups - logger - .error("setMembership - failed to post activity: unexpected authority type: " - + AuthorityType - .getAuthorityType(userName)); + logger.error("setMembership - failed to post activity: unexpected authority type: " + + AuthorityType.getAuthorityType(authorityName)); } } - } else + } + else { // Raise a permission exception - throw new AlfrescoRuntimeException( - "Access denied, user does not have permissions to modify membership details of the site '" - + shortName + "'"); + throw new SiteServiceException(MSG_CAN_NOT_CHANGE_MSHIP, new Object[]{shortName}); } } } /** - * @see org.alfresco.repo.site.SiteService#createContainer(java.lang.String, + * @see org.alfresco.service.cmr.site.SiteService#createContainer(java.lang.String, * java.lang.String, org.alfresco.service.namespace.QName, * java.util.Map) */ - public NodeRef createContainer(String shortName, String componentId, - QName containerType, Map containerProperties) + public NodeRef createContainer(String shortName, + String componentId, + QName containerType, + Map containerProperties) { // Check for the component id - if (componentId == null || componentId.length() == 0) - { - throw new AlfrescoRuntimeException("Component id not provided"); - } + ParameterCheck.mandatoryString("componentId", componentId); // retrieve site NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } // retrieve component folder within site @@ -993,7 +1104,8 @@ public class SiteServiceImpl implements SiteService, SiteModel try { containerNodeRef = findContainer(siteNodeRef, componentId); - } catch (FileNotFoundException e) + } + catch (FileNotFoundException e) { } @@ -1036,21 +1148,17 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#getContainer(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#getContainer(java.lang.String) */ public NodeRef getContainer(String shortName, String componentId) { - if (componentId == null || componentId.length() == 0) - { - throw new AlfrescoRuntimeException("Component id not provided"); - } + ParameterCheck.mandatoryString("componentId", componentId); // retrieve site NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } // retrieve component folder within site @@ -1059,7 +1167,8 @@ public class SiteServiceImpl implements SiteService, SiteModel try { containerNodeRef = findContainer(siteNodeRef, componentId); - } catch (FileNotFoundException e) + } + catch (FileNotFoundException e) { } @@ -1067,21 +1176,17 @@ public class SiteServiceImpl implements SiteService, SiteModel } /** - * @see org.alfresco.repo.site.SiteService#hasContainer(java.lang.String) + * @see org.alfresco.service.cmr.site.SiteService#hasContainer(java.lang.String) */ public boolean hasContainer(String shortName, String componentId) { - if (componentId == null || componentId.length() == 0) - { - throw new AlfrescoRuntimeException("Component id not provided"); - } + ParameterCheck.mandatoryString("componentId", componentId); // retrieve site NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) { - throw new AlfrescoRuntimeException("Site " + shortName - + " does not exist."); + throw new SiteServiceException(MSG_SITE_NO_EXIST, new Object[]{shortName}); } // retrieve component folder within site @@ -1091,7 +1196,8 @@ public class SiteServiceImpl implements SiteService, SiteModel { findContainer(siteNodeRef, componentId); hasContainer = true; - } catch (FileNotFoundException e) + } + catch (FileNotFoundException e) { } @@ -1117,12 +1223,18 @@ public class SiteServiceImpl implements SiteService, SiteModel paths); if (!fileInfo.isFolder()) { - throw new AlfrescoRuntimeException("Site container " - + fileInfo.getName() + " does not refer to a folder "); + throw new SiteServiceException(MSG_SITE_CONTAINER_NOT_FOLDER, new Object[]{fileInfo.getName()}); } return fileInfo.getNodeRef(); } + /** + * Helper method to get the activity data for a user + * + * @param userName user name + * @param role role + * @return + */ private String getActivityData(String userName, String role) { String memberFN = ""; diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java index de1e49aecd..abfd21e73a 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java @@ -34,11 +34,19 @@ import org.alfresco.model.ContentModel; import org.alfresco.model.ForumModel; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; 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.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.namespace.QName; import org.alfresco.util.BaseAlfrescoSpringTest; @@ -59,6 +67,8 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest private static final String USER_ONE = "UserOne_SiteServiceImplTest"; private static final String USER_TWO = "UserTwo_SiteServiceImplTest"; private static final String USER_THREE = "UserThree_SiteServiceImplTest"; + private static final String GROUP_ONE = "GrpOne_SiteServiceImplTest"; + private static final String GROUP_TWO = "GrpTwo_SiteServiceImplTest"; private SiteService siteService; private ScriptService scriptService; @@ -66,6 +76,11 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest private AuthenticationComponent authenticationComponent; private TaggingService taggingService; private PersonService personService; + private AuthorityService authorityService; + private FileFolderService fileFolderService; + + private String groupOne; + private String groupTwo; /** * Called during the transaction setup @@ -81,11 +96,22 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest this.authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent"); this.taggingService = (TaggingService)this.applicationContext.getBean("TaggingService"); this.personService = (PersonService)this.applicationContext.getBean("PersonService"); + this.authorityService = (AuthorityService)this.applicationContext.getBean("AuthorityService"); + this.fileFolderService = (FileFolderService)this.applicationContext.getBean("FileFolderService"); - // Do the test's as userOne + // Create the test users createUser(USER_ONE); createUser(USER_TWO); createUser(USER_THREE); + + // Create the test groups + this.groupOne = this.authorityService.createAuthority(AuthorityType.GROUP, null, GROUP_ONE); + this.authorityService.addAuthority(this.groupOne, USER_TWO); + this.groupTwo = this.authorityService.createAuthority(AuthorityType.GROUP, null, GROUP_TWO); + this.authorityService.addAuthority(this.groupTwo, USER_TWO); + this.authorityService.addAuthority(this.groupTwo, USER_THREE); + + // Set the current authentication this.authenticationComponent.setCurrentUser(USER_ONE); } @@ -109,25 +135,25 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testCreateSite() throws Exception { // Create a public site - SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); String name = "!£$%^&*()_+=-[]{}"; - siteInfo = this.siteService.createSite(TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); + siteInfo = this.siteService.createSite(TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); siteInfo = this.siteService.getSite(name); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); name = "éíóúÁÉÍÓÚ"; - siteInfo = this.siteService.createSite(TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); + siteInfo = this.siteService.createSite(TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); siteInfo = this.siteService.getSite(name); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, name, TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); // Test for duplicate site error try { - this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); + this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); fail("Shouldn't allow duplicate site short names."); } catch (AlfrescoRuntimeException exception) @@ -138,8 +164,8 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testETHREEOH_15() throws Exception { - SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); authenticationComponent.setCurrentUser("admin"); this.siteService.setMembership(siteInfo.getShortName(), USER_TWO, SiteModel.SITE_MANAGER); @@ -170,15 +196,19 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest this.siteService.removeMembership(siteInfo.getShortName(), USER_THREE); } - private void checkSiteInfo( SiteInfo siteInfo, String expectedSitePreset, String expectedShortName, String expectedTitle, - String expectedDescription, boolean expectedIsPublic) + private void checkSiteInfo(SiteInfo siteInfo, + String expectedSitePreset, + String expectedShortName, + String expectedTitle, + String expectedDescription, + SiteVisibility expectedVisibility) { assertNotNull(siteInfo); assertEquals(expectedSitePreset, siteInfo.getSitePreset()); assertEquals(expectedShortName, siteInfo.getShortName()); assertEquals(expectedTitle, siteInfo.getTitle()); assertEquals(expectedDescription, siteInfo.getDescription()); - assertEquals(expectedIsPublic, siteInfo.getIsPublic()); + assertEquals(expectedVisibility, siteInfo.getVisibility()); assertNotNull(siteInfo.getNodeRef()); // Check that the site is a tag scope @@ -197,10 +227,10 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest assertTrue(sites.isEmpty()); // Create some sites - this.siteService.createSite(TEST_SITE_PRESET, "mySiteOne", TEST_TITLE, TEST_DESCRIPTION, true); - this.siteService.createSite(TEST_SITE_PRESET, "mySiteTwo", TEST_TITLE, TEST_DESCRIPTION, false); - this.siteService.createSite(TEST_SITE_PRESET_2, "mySiteThree", TEST_TITLE, TEST_DESCRIPTION, true); - this.siteService.createSite(TEST_SITE_PRESET_2, "mySiteFour", TEST_TITLE, TEST_DESCRIPTION, false); + this.siteService.createSite(TEST_SITE_PRESET, "mySiteOne", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + this.siteService.createSite(TEST_SITE_PRESET, "mySiteTwo", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); + this.siteService.createSite(TEST_SITE_PRESET_2, "mySiteThree", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + this.siteService.createSite(TEST_SITE_PRESET_2, "mySiteFour", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); // Get all the sites sites = this.siteService.listSites(null, null); @@ -212,19 +242,19 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest String shortName = site.getShortName(); if (shortName.equals("mySiteOne") == true) { - checkSiteInfo(site, TEST_SITE_PRESET, "mySiteOne", TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(site, TEST_SITE_PRESET, "mySiteOne", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); } else if (shortName.equals("mySiteTwo") == true) { - checkSiteInfo(site, TEST_SITE_PRESET, "mySiteTwo", TEST_TITLE, TEST_DESCRIPTION, false); + checkSiteInfo(site, TEST_SITE_PRESET, "mySiteTwo", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); } else if (shortName.equals("mySiteThree") == true) { - checkSiteInfo(site, TEST_SITE_PRESET_2, "mySiteThree", TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(site, TEST_SITE_PRESET_2, "mySiteThree", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); } else if (shortName.equals("mySiteFour") == true) { - checkSiteInfo(site, TEST_SITE_PRESET_2, "mySiteFour", TEST_TITLE, TEST_DESCRIPTION, false); + checkSiteInfo(site, TEST_SITE_PRESET_2, "mySiteFour", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); } else { @@ -256,17 +286,17 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest assertNull(siteInfo); // Create a test site - this.siteService.createSite(TEST_SITE_PRESET, "testGetSite", TEST_TITLE, TEST_DESCRIPTION, true); + this.siteService.createSite(TEST_SITE_PRESET, "testGetSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); // Get the test site siteInfo = this.siteService.getSite("testGetSite"); assertNotNull(siteInfo); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testGetSite", TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testGetSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); } public void testUpdateSite() { - SiteInfo siteInfo = new SiteInfo(TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", false, null); + SiteInfo siteInfo = new SiteInfoImpl(TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", SiteVisibility.PRIVATE, null); // update a site that isn't there try @@ -280,17 +310,17 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest } // Create a test site - this.siteService.createSite(TEST_SITE_PRESET, "testUpdateSite", TEST_TITLE, TEST_DESCRIPTION, true); + this.siteService.createSite(TEST_SITE_PRESET, "testUpdateSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); // Update the details of the site this.siteService.updateSite(siteInfo); siteInfo = this.siteService.getSite("testUpdateSite"); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", false); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", SiteVisibility.PRIVATE); // Update the permission again - siteInfo.setIsPublic(true); + siteInfo.setVisibility(SiteVisibility.PUBLIC); this.siteService.updateSite(siteInfo); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", true); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testUpdateSite", "changedTitle", "changedDescription", SiteVisibility.PUBLIC); } public void testDeleteSite() @@ -307,7 +337,7 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest } // Create a test site - this.siteService.createSite(TEST_SITE_PRESET, "testUpdateSite", TEST_TITLE, TEST_DESCRIPTION, true); + this.siteService.createSite(TEST_SITE_PRESET, "testUpdateSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); assertNotNull(this.siteService.getSite("testUpdateSite")); // Delete the site @@ -318,8 +348,8 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testIsPublic() { // Create a couple of sites as user one - this.siteService.createSite(TEST_SITE_PRESET, "isPublicTrue", TEST_TITLE, TEST_DESCRIPTION, true); - this.siteService.createSite(TEST_SITE_PRESET, "isPublicFalse", TEST_TITLE, TEST_DESCRIPTION, false); + this.siteService.createSite(TEST_SITE_PRESET, "isPublicTrue", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + this.siteService.createSite(TEST_SITE_PRESET, "isPublicFalse", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); // Get the sites as user one List sites = this.siteService.listSites(null, null); @@ -331,7 +361,7 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest sites = this.siteService.listSites(null, null); assertNotNull(sites); assertEquals(1, sites.size()); - checkSiteInfo(sites.get(0), TEST_SITE_PRESET, "isPublicTrue", TEST_TITLE, TEST_DESCRIPTION, true); + checkSiteInfo(sites.get(0), TEST_SITE_PRESET, "isPublicTrue", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); // Make user 2 a member of the site //TestWithUserUtils.authenticateUser(USER_ONE, "PWD", this.authenticationService, this.authenticationComponent); @@ -348,7 +378,7 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testMembership() { // Create a site as user one - this.siteService.createSite(TEST_SITE_PRESET, "testMembership", TEST_TITLE, TEST_DESCRIPTION, false); + this.siteService.createSite(TEST_SITE_PRESET, "testMembership", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); // Get the members of the site and check that user one is a manager Map members = this.siteService.listMembers("testMembership", null, null); @@ -477,8 +507,8 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testJoinLeave() { // Create a site as user one - this.siteService.createSite(TEST_SITE_PRESET, "testMembership", TEST_TITLE, TEST_DESCRIPTION, true); - this.siteService.createSite(TEST_SITE_PRESET, "testMembershipPrivate", TEST_TITLE, TEST_DESCRIPTION, false); + this.siteService.createSite(TEST_SITE_PRESET, "testMembership", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + this.siteService.createSite(TEST_SITE_PRESET, "testMembershipPrivate", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); // Become user two //TestWithUserUtils.authenticateUser(USER_TWO, "PWD", this.authenticationService, this.authenticationComponent); @@ -545,7 +575,7 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest public void testContainer() { // Create a couple of sites as user one - SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "testContainer", TEST_TITLE, TEST_DESCRIPTION, true); + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "testContainer", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); boolean hasContainer = this.siteService.hasContainer(siteInfo.getShortName(), "folder.component"); assertFalse(hasContainer); @@ -614,8 +644,8 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest QName additionalInformationQName = QName.createQName(SiteModel.SITE_CUSTOM_PROPERTY_URL, "additionalInformation"); // Create a site - SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); - checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, true); + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); assertNull(siteInfo.getCustomProperty(additionalInformationQName)); assertNotNull(siteInfo.getCustomProperties()); assertTrue(siteInfo.getCustomProperties().isEmpty()); @@ -637,12 +667,239 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest } + public void testGroupMembership() + { + // USER_ONE - SiteAdmin + // GROUP_ONE - USER_TWO + // GROUP_TWO - USER_TWO, USER_THREE + + // Create a site as user one + this.siteService.createSite(TEST_SITE_PRESET, "testMembership", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); + + // Get the members of the site and check that user one is a manager + Map members = this.siteService.listMembers("testMembership", null, null); + assertNotNull(members); + assertEquals(1, members.size()); + assertTrue(members.containsKey(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, members.get(USER_ONE)); + + // Add a group + this.siteService.setMembership("testMembership", this.groupTwo, SiteModel.SITE_CONSUMER); + // - is the group in the list of all members? + members = this.siteService.listMembers("testMembership", null, null); + assertNotNull(members); + assertEquals(2, members.size()); + assertTrue(members.containsKey(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, members.get(USER_ONE)); + assertTrue(members.containsKey(this.groupTwo)); + assertEquals(SiteModel.SITE_CONSUMER, members.get(this.groupTwo)); + // - is the user in the expanded list? + members = this.siteService.listMembers("testMembership", null, null, true); + assertNotNull(members); + assertEquals(3, members.size()); + assertTrue(members.containsKey(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, members.get(USER_ONE)); + assertTrue(members.containsKey(USER_TWO)); + assertEquals(SiteModel.SITE_CONSUMER, members.get(USER_TWO)); + assertTrue(members.containsKey(USER_THREE)); + assertEquals(SiteModel.SITE_CONSUMER, members.get(USER_THREE)); + // - is the user a member? + assertTrue(this.siteService.isMember("testMembership", USER_ONE)); + assertTrue(this.siteService.isMember("testMembership", USER_TWO)); + assertTrue(this.siteService.isMember("testMembership", USER_THREE)); + // - is the group a member? + assertTrue(this.siteService.isMember("testMembership", this.groupTwo)); + // - can we get the roles for the various members directly + assertEquals(SiteModel.SITE_MANAGER, this.siteService.getMembersRole("testMembership", USER_ONE)); + assertEquals(SiteModel.SITE_CONSUMER, this.siteService.getMembersRole("testMembership", USER_TWO)); + assertEquals(SiteModel.SITE_CONSUMER, this.siteService.getMembersRole("testMembership", USER_THREE)); + assertEquals(SiteModel.SITE_CONSUMER, this.siteService.getMembersRole("testMembership", this.groupTwo)); + + // Add a group member as an explicit member + this.siteService.setMembership("testMembership", USER_THREE, SiteModel.SITE_COLLABORATOR); + // - check the explicit members list + members = this.siteService.listMembers("testMembership", null, null); + assertNotNull(members); + assertEquals(3, members.size()); + assertTrue(members.containsKey(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, members.get(USER_ONE)); + assertTrue(members.containsKey(USER_THREE)); + assertEquals(SiteModel.SITE_COLLABORATOR, members.get(USER_THREE)); + assertTrue(members.containsKey(this.groupTwo)); + assertEquals(SiteModel.SITE_CONSUMER, members.get(this.groupTwo)); + // - check the expanded members list + members = this.siteService.listMembers("testMembership", null, null, true); + assertNotNull(members); + assertEquals(3, members.size()); + assertTrue(members.containsKey(USER_ONE)); + assertEquals(SiteModel.SITE_MANAGER, members.get(USER_ONE)); + assertTrue(members.containsKey(USER_TWO)); + assertEquals(SiteModel.SITE_CONSUMER, members.get(USER_TWO)); + assertTrue(members.containsKey(USER_THREE)); + assertEquals(SiteModel.SITE_COLLABORATOR, members.get(USER_THREE)); + // - check is member + assertTrue(this.siteService.isMember("testMembership", USER_ONE)); + assertTrue(this.siteService.isMember("testMembership", USER_TWO)); + assertTrue(this.siteService.isMember("testMembership", USER_THREE)); + // - is the group a member? + assertTrue(this.siteService.isMember("testMembership", this.groupTwo)); + // - check get role directly + assertEquals(SiteModel.SITE_MANAGER, this.siteService.getMembersRole("testMembership", USER_ONE)); + assertEquals(SiteModel.SITE_CONSUMER, this.siteService.getMembersRole("testMembership", USER_TWO)); + assertEquals(SiteModel.SITE_COLLABORATOR, this.siteService.getMembersRole("testMembership", USER_THREE)); + assertEquals(SiteModel.SITE_CONSUMER, this.siteService.getMembersRole("testMembership", this.groupTwo)); + + // Check permissions of added group + + // Update the permissions of the group + + // Add other group with higher role + // - is group in list? + // - is new user a member? + // - does redefined user have highest role? + + // Add group user as a specific user with higher role + // - check that the user's role is higher that the group? + + // Add a group with a sub-group + + // Remove groups + } + + /** + * Tests the visibility of a site + * + * See https://issues.alfresco.com/jira/browse/JAWS-291 + */ + public void testSiteVisibility() + { + // Create a public site + SiteInfo siteInfo = createTestSiteWithContent("testSiteVisibilityPublicSite", "testComp", SiteVisibility.PUBLIC); + // - is the value on the site nodeRef correct? + assertEquals(SiteVisibility.PUBLIC.toString(), this.nodeService.getProperty(siteInfo.getNodeRef(), SiteModel.PROP_SITE_VISIBILITY)); + // - is the site info correct? + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityPublicSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + siteInfo = this.siteService.getSite("testSiteVisibilityPublicSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityPublicSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + // - are the permissions correct for non-members? + testVisibilityPermissions("Testing visibility of public site", USER_TWO, siteInfo, true, true); + + // Create a moderated site + siteInfo = createTestSiteWithContent("testSiteVisibilityModeratedSite", "testComp", SiteVisibility.MODERATED); + // - is the value on the site nodeRef correct? + assertEquals(SiteVisibility.MODERATED.toString(), this.nodeService.getProperty(siteInfo.getNodeRef(), SiteModel.PROP_SITE_VISIBILITY)); + // - is the site info correct? + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityModeratedSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.MODERATED); + siteInfo = this.siteService.getSite("testSiteVisibilityModeratedSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityModeratedSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.MODERATED); + // - are the permissions correct for non-members? + testVisibilityPermissions("Testing visibility of moderated site", USER_TWO, siteInfo, true, false); + + // Create a private site + siteInfo = createTestSiteWithContent("testSiteVisibilityPrivateSite", "testComp", SiteVisibility.PRIVATE); + // - is the value on the site nodeRef correct? + assertEquals(SiteVisibility.PRIVATE.toString(), this.nodeService.getProperty(siteInfo.getNodeRef(), SiteModel.PROP_SITE_VISIBILITY)); + // - is the site info correct? + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityPrivateSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); + siteInfo = this.siteService.getSite("testSiteVisibilityPrivateSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityPrivateSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); + // - are the permissions correct for non-members? + testVisibilityPermissions("Testing visibility of private site", USER_TWO, siteInfo, false, false); + + SiteInfo changeSite = createTestSiteWithContent("testSiteVisibilityChangeSite", "testComp", SiteVisibility.PUBLIC); + // Switch from public -> moderated + changeSite.setVisibility(SiteVisibility.MODERATED); + this.siteService.updateSite(changeSite); + // - check the updated sites visibility + siteInfo = this.siteService.getSite("testSiteVisibilityChangeSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityChangeSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.MODERATED); + testVisibilityPermissions("Testing visibility of moderated site", USER_TWO, siteInfo, true, false); + + // Switch from moderated -> private + changeSite.setVisibility(SiteVisibility.PRIVATE); + this.siteService.updateSite(changeSite); + // - check the updated sites visibility + siteInfo = this.siteService.getSite("testSiteVisibilityChangeSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityChangeSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PRIVATE); + testVisibilityPermissions("Testing visibility of moderated site", USER_TWO, siteInfo, false, false); + + // Switch from private -> public + changeSite.setVisibility(SiteVisibility.PUBLIC); + this.siteService.updateSite(changeSite); + // - check the updated sites visibility + siteInfo = this.siteService.getSite("testSiteVisibilityChangeSite"); + checkSiteInfo(siteInfo, TEST_SITE_PRESET, "testSiteVisibilityChangeSite", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); + testVisibilityPermissions("Testing visibility of moderated site", USER_TWO, siteInfo, true, true); + } + + private SiteInfo createTestSiteWithContent(String shortName, String compName, SiteVisibility visibility) + { + // Create a public site + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, + shortName, + TEST_TITLE, + TEST_DESCRIPTION, + visibility); + NodeRef foldeRef = this.siteService.createContainer(shortName, compName, ContentModel.TYPE_FOLDER, null); + FileInfo fileInfo = this.fileFolderService.create(foldeRef, "test.txt", ContentModel.TYPE_CONTENT); + ContentWriter writer = this.fileFolderService.getWriter(fileInfo.getNodeRef()); + writer.putContent("Just some old content that doesn't mean anything"); + + return siteInfo; + } + + private void testVisibilityPermissions(String message, String userName, SiteInfo siteInfo, boolean listSite, boolean readSite) + { + String holdUser = this.authenticationComponent.getCurrentUserName(); + this.authenticationComponent.setCurrentUser(userName); + try + { + // Can the site be seen in the list sites by the user? + List sites = this.siteService.listSites(null, null); + boolean siteInList = sites.contains(siteInfo); + if (listSite == true && siteInList == false) + { + fail(message + ": The site '" + siteInfo.getShortName() + "' was expected in the list of sites for user '" + userName + "'"); + } + else if (listSite == false && siteInList == true) + { + fail(message + ": The site '" + siteInfo.getShortName() + "' was NOT expected in the list of sites for user '" + userName + "'"); + } + + if (siteInList == true) + { + // Can site content be read by the user? + NodeRef folder = this.siteService.getContainer(siteInfo.getShortName(), "testComp"); + List files = null; + try + { + files = this.fileFolderService.listFiles(folder); + if (readSite == false) + { + fail(message + ": Content of the site '" + siteInfo.getShortName() + "' was NOT expected to be read by user '" + userName + "'"); + } + } + catch (Exception exception) + { + if (readSite == true) + { + fail(message + ": Content of the site '" + siteInfo.getShortName() + "' was expected to be read by user '" + userName + "'"); + } + } + } + } + finally + { + this.authenticationComponent.setCurrentUser(holdUser); + } + } + // == Test the JavaScript API == public void testJSAPI() throws Exception { // Create a site with a custom property - SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteWithCustomProperty", TEST_TITLE, TEST_DESCRIPTION, true); + SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteWithCustomProperty", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC); NodeRef siteNodeRef = siteInfo.getNodeRef(); Map properties = new HashMap(1); properties.put(QName.createQName(SiteModel.SITE_CUSTOM_PROPERTY_URL, "additionalInformation"), "information"); diff --git a/source/java/org/alfresco/repo/site/script/ScriptSiteService.java b/source/java/org/alfresco/repo/site/script/ScriptSiteService.java index 77c35da12d..3d80f29c31 100644 --- a/source/java/org/alfresco/repo/site/script/ScriptSiteService.java +++ b/source/java/org/alfresco/repo/site/script/ScriptSiteService.java @@ -28,9 +28,11 @@ import java.util.ArrayList; import java.util.List; import org.alfresco.repo.jscript.BaseScopableProcessorExtension; -import org.alfresco.repo.site.SiteInfo; -import org.alfresco.repo.site.SiteService; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.util.ParameterCheck; /** @@ -40,6 +42,11 @@ import org.alfresco.service.ServiceRegistry; */ public class ScriptSiteService extends BaseScopableProcessorExtension { + /** Visibility helper constants */ + public static final String PUBLIC_SITE = "PUBLIC"; + public static final String MODERATED_SITE = "MODERATED"; + public static final String PRIVATE_SITE = "PRIVATE"; + /** Service Registry */ private ServiceRegistry serviceRegistry; @@ -66,6 +73,23 @@ public class ScriptSiteService extends BaseScopableProcessorExtension this.siteService = siteService; } + /** + * @see {@link #createSite(String, String, String, String, String)} + * + * @param sitePreset site preset + * @param shortName site short name + * @param title site title + * @param description site description + * @param isPublic whether the site is public or not + * @return Site the created site + * @deprecated as of version 3.2, replaced by {@link #createSite(String, String, String, String, String)} + */ + public Site createSite(String sitePreset, String shortName, String title, String description, boolean isPublic) + { + SiteInfo siteInfo = this.siteService.createSite(sitePreset, shortName, title, description, isPublic); + return new Site(siteInfo, this.serviceRegistry, this.siteService, getScope()); + } + /** * Create a new site. *

@@ -74,13 +98,15 @@ public class ScriptSiteService extends BaseScopableProcessorExtension * @param sitePreset site preset * @param shortName site short name * @param title site title - * @param description site description - * @param isPublic whether the site is public or not + * @param description site description + * @param visibility visibility of the site (public|moderated|private) * @return Site the created site */ - public Site createSite(String sitePreset, String shortName, String title, String description, boolean isPublic) - { - SiteInfo siteInfo = this.siteService.createSite(sitePreset, shortName, title, description, isPublic); + public Site createSite(String sitePreset, String shortName, String title, String description, String visibility) + { + ParameterCheck.mandatoryString("visibility", visibility); + SiteVisibility siteVisibility = SiteVisibility.valueOf(visibility); + SiteInfo siteInfo = this.siteService.createSite(sitePreset, shortName, title, description, siteVisibility); return new Site(siteInfo, this.serviceRegistry, this.siteService, getScope()); } @@ -142,7 +168,7 @@ public class ScriptSiteService extends BaseScopableProcessorExtension } /** - * Returns an array of all the roles that can be assigned to a memeber of a site. + * Returns an array of all the roles that can be assigned to a member of a site. * * @return String[] roles available to assign to a member of a site */ diff --git a/source/java/org/alfresco/repo/site/script/Site.java b/source/java/org/alfresco/repo/site/script/Site.java index 382390c61e..1382fd1ff5 100644 --- a/source/java/org/alfresco/repo/site/script/Site.java +++ b/source/java/org/alfresco/repo/site/script/Site.java @@ -37,14 +37,16 @@ import org.alfresco.repo.jscript.ScriptNode.NodeValueConverter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.repo.site.SiteInfo; import org.alfresco.repo.site.SiteModel; -import org.alfresco.repo.site.SiteService; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; @@ -158,7 +160,8 @@ public class Site implements Serializable /** * Gets whether the site is public or not * - * @return true is public false otherwise + * @return true is public false otherwise + * @deprecated since version 3.2, replaced by {@link #getVisibility()} */ public boolean getIsPublic() { @@ -169,6 +172,7 @@ public class Site implements Serializable * Set whether the site is public or not * * @param isPublic true the site is public false otherwise + * @deprecated since version 3.2, replaced by {@link #setVisibility(String)} */ public void setIsPublic(boolean isPublic) { @@ -176,6 +180,29 @@ public class Site implements Serializable this.siteInfo.setIsPublic(isPublic); } + /** + * Get the site visibility + * + * @return String site visibility + */ + public String getVisibility() + { + return this.siteInfo.getVisibility().toString(); + } + + /** + * Set the site visibility + * + * @param visibility site visibility (public|moderated|private) + */ + public void setVisibility(String visibility) + { + ParameterCheck.mandatoryString("visibility", visibility); + SiteVisibility siteVisibility = SiteVisibility.valueOf(visibility); + this.siteInfo.setVisibility(siteVisibility); + this.isDirty = true; + } + /** * Get the site node, null if none * diff --git a/source/java/org/alfresco/repo/site/script/test_siteService.js b/source/java/org/alfresco/repo/site/script/test_siteService.js index e34911d6f4..b00c14ea2e 100644 --- a/source/java/org/alfresco/repo/site/script/test_siteService.js +++ b/source/java/org/alfresco/repo/site/script/test_siteService.js @@ -1,11 +1,11 @@ -function checkSite(site, sitePreset, shortName, title, description, isPublic) +function checkSite(site, sitePreset, shortName, title, description, visibility) { test.assertNotNull(site); test.assertEquals(sitePreset, site.sitePreset); test.assertEquals(shortName, site.shortName); test.assertEquals(title, site.title); test.assertEquals(description, site.description); - test.assertEquals(isPublic, site.isPublic); + test.assertEquals(visibility, site.visibility); test.assertNotNull(site.node); test.assertTrue(site.node.isTagScope); } @@ -17,21 +17,21 @@ function testCRUD() test.assertNull(site, "Site should not have been found."); // Try and create a site - site = siteService.createSite("sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", true); - checkSite(site, "sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", true); + site = siteService.createSite("sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", siteService.PUBLIC_SITE); + checkSite(site, "sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", siteService.PUBLIC_SITE); // Try and get the created site site = siteService.getSite("siteShortNameCRUD"); - checkSite(site, "sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", true); + checkSite(site, "sitePreset", "siteShortNameCRUD", "siteTitle", "siteDescription", siteService.PUBLIC_SITE); // Try and update the values of the site site.title = "abc123abc"; site.description = "abc123abc"; - site.isPublic = false; - checkSite(site, "sitePreset", "siteShortNameCRUD", "abc123abc", "abc123abc", false); + site.visibility = siteService.PRIVATE_SITE; + checkSite(site, "sitePreset", "siteShortNameCRUD", "abc123abc", "abc123abc", siteService.PRIVATE_SITE); site.save(); site = siteService.getSite("siteShortNameCRUD"); - checkSite(site, "sitePreset", "siteShortNameCRUD", "abc123abc", "abc123abc", false); + checkSite(site, "sitePreset", "siteShortNameCRUD", "abc123abc", "abc123abc", siteService.PRIVATE_SITE); // Delete the site site.deleteSite(); @@ -42,8 +42,8 @@ function testCRUD() function testListSites() { // Create a couple of sites - siteService.createSite("sitePreset", "siteShortName", "siteTitle", "siteDescription", true); - siteService.createSite("sitePreset", "siteShortName2", "siteTitle", "siteDescription", true); + siteService.createSite("sitePreset", "siteShortName", "siteTitle", "siteDescription", siteService.PUBLIC_SITE); + siteService.createSite("sitePreset", "siteShortName2", "siteTitle", "siteDescription", siteService.PUBLIC_SITE); // List all the site var sites = siteService.listSites(null, null); @@ -133,7 +133,7 @@ function testContainer() function testPermissions() { - var site = siteService.createSite("sitePreset", "siteShortNameToo", "siteTitle", "siteDescription", false); + var site = siteService.createSite("sitePreset", "siteShortNameToo", "siteTitle", "siteDescription", siteService.PRIVATE_SITE); test.assertNotNull(site); var container = site.createContainer("test.permissions"); test.assertNotNull(container); @@ -164,7 +164,7 @@ function testRolesAndGroups() test.assertNotNull(roles); test.assertFalse(roles.length == 0); - var site = siteService.createSite("sitePreset", "sn", "siteTitle", "siteDescription", false); + var site = siteService.createSite("sitePreset", "sn", "siteTitle", "siteDescription", siteService.PRIVATE_SITE); var siteGroup = site.siteGroup; test.assertNotNull(siteGroup); test.assertEquals("GROUP_site_sn", siteGroup); diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index b4d86dd386..051449a777 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -28,7 +28,6 @@ import java.util.Collection; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.forms.FormService; -import org.alfresco.repo.site.SiteService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.attributes.AttributeService; @@ -39,6 +38,7 @@ import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.invitation.InvitationService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.EditionService; @@ -59,6 +59,7 @@ import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.version.VersionService; @@ -135,6 +136,7 @@ public interface ServiceRegistry static final QName SANDBOX_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SandboxService"); static final QName ASSET_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AssetService"); static final QName FORM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FormService"); + static final QName INVITATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "InvitationService"); /** * Get the list of services provided by the Repository @@ -456,4 +458,11 @@ public interface ServiceRegistry */ @NotAuditable FormService getFormService(); + + /** + * Get the invitation service (or null if one is not provided) + * @return the invitation service + */ + @NotAuditable + InvitationService getInvitationService(); } diff --git a/source/java/org/alfresco/service/cmr/invitation/Invitation.java b/source/java/org/alfresco/service/cmr/invitation/Invitation.java new file mode 100644 index 0000000000..8fa29130de --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/Invitation.java @@ -0,0 +1,73 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + + +/** + * The invitation request is a command object for who, needs to be added or removed + * from which resource with which attributes. + * + * Invitations are processed by the InvitationService + * + * @see org.alfresco.service.cmr.invitation.InvitationService + * + * @author mrogers + */ +public interface Invitation +{ + /** + * What sort of Resource Web Project, Web Site, Node + * (Just Web site for now) + */ + enum ResourceType + { + WEB_SITE + } + + /** + * What sort of resource is it, for example a WEB_SITE? + * @return the resource type + */ + public ResourceType getResourceType(); + + /** + * What is the resource name ? + * @return the name of the resource + */ + public String getResourceName(); + + /** + * Who is this invitation for ? + * @return the user name of the invitee + */ + public String getInviteeUserName(); + + /** + * What is the unique reference for this invitation ? + * @return the unique reference for this invitation + */ + public String getInviteId(); + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationException.java b/source/java/org/alfresco/service/cmr/invitation/InvitationException.java new file mode 100644 index 0000000000..7053971c54 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationException.java @@ -0,0 +1,40 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Thrown when there is a problem with an invitation. + */ +public class InvitationException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = -3925105163386197586L; + + public InvitationException(String msgId, Object ... args) + { + super(msgId, args); + } +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionForbidden.java b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionForbidden.java new file mode 100644 index 0000000000..9b325163e4 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionForbidden.java @@ -0,0 +1,47 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +/** + * The current user has attempted to do something that they do not have + * the rights to do. + */ +public class InvitationExceptionForbidden extends InvitationException +{ + + public InvitationExceptionForbidden(String msg, Object[] args) { + super(msg, args); + } + + public InvitationExceptionForbidden(String msgId) { + super(msgId); + } + + /** + * + */ + private static final long serialVersionUID = -3083631235637184401L; + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionNotFound.java b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionNotFound.java new file mode 100644 index 0000000000..0686eff17b --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionNotFound.java @@ -0,0 +1,43 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +/** + * The invitation does not exist. + */ +public class InvitationExceptionNotFound extends InvitationException +{ + + public InvitationExceptionNotFound(String msgId, Object[] args) + { + super(msgId, args); + } + + /** + * + */ + private static final long serialVersionUID = -6112400396903083597L; + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionUserError.java b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionUserError.java new file mode 100644 index 0000000000..590b9370dc --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationExceptionUserError.java @@ -0,0 +1,43 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +/** + * The current user has attempted to do something that is not valid. + */ +public class InvitationExceptionUserError extends InvitationException +{ + + public InvitationExceptionUserError(String msgId, Object[] args) + { + super(msgId, args); + } + + /** + * + */ + private static final long serialVersionUID = -6112400396903083597L; + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationSearchCriteria.java b/source/java/org/alfresco/service/cmr/invitation/InvitationSearchCriteria.java new file mode 100644 index 0000000000..941192402e --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationSearchCriteria.java @@ -0,0 +1,50 @@ +package org.alfresco.service.cmr.invitation; + +/** + * Search criteria for invitation service + * + */ +public interface InvitationSearchCriteria +{ + /** + * What type of invitations to search for ? + * + */ + public enum InvitationType + { + ALL, + MODERATED, + NOMINATED + } + + + /** + * Search by inviter (who started the invitation) + * @return + */ + String getInviter(); + + /** + * Search by invitee (who is being invited, alfresco userid) + * @return + */ + String getInvitee(); + + /** + * Search by resource name + * @return the resource name + */ + String getResourceName(); + + /** + * Search by resource type + * @return the resource type + */ + Invitation.ResourceType getResourceType(); + + /** + * Do you want to search for moderated, nominated or all invitations ? + * @return the type to search for. + */ + InvitationType getInvitationType(); +} diff --git a/source/java/org/alfresco/service/cmr/invitation/InvitationService.java b/source/java/org/alfresco/service/cmr/invitation/InvitationService.java new file mode 100644 index 0000000000..2d803379d5 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/InvitationService.java @@ -0,0 +1,151 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +import java.util.List; + +/** + * The invitation service provides the ability to invite + * people to resources. For example adding a user to a shared web site. + * + * It manages the relationship between person, resource and requestType + * and may also pass along information such as who is to approve or the expected + * role of the user. + * + * @author mrogers + */ +public interface InvitationService +{ + + /** + * Start the invitation process for a NominatedInvitation + * + * @param inviteeFirstName + * @param inviteeLastName + * @param inviteeEmail + * @param inviteeUserName the alfresco user name of the invitee, may be null for a new user + * @param Invitation.ResourceType resourceType + * @param resourceName + * @param inviteeRole + * @param serverPath + * @param acceptUrl + * @param rejectUrl + * + * @return the nominated invitation which will contain the invitationId and ticket which + * will uniquely identify this invitation. + * + * @throws InvitationException + * @throws InvitationExceptionUserError + * @throws InvitationExceptionForbidden + */ + public NominatedInvitation inviteNominated( + String inviteeFirstName, + String inviteeLastName, + String inviteeEmail, + String inviteeUserName, + Invitation.ResourceType resourceType, + String resourceName, + String inviteeRole, + String serverPath, + String acceptUrl, + String rejectUrl); + + /** + * 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 ? + */ + public ModeratedInvitation inviteModerated( + String inviteeComments, + String inviteeUserName, + Invitation.ResourceType resourceType, + String resourceName, + String inviteeRole); + + /** + * For a Nominated Invitation invitee accepts this invitation + * + * @param request + * @param ticket + * @return the invitation + */ + public Invitation accept(String invitationId, String ticket); + + + /** + * Moderator approves this invitation + * + * @param invitationId the request to approve + * @param reason - comments about the acceptance + */ + public Invitation approve(String invitationId, String reason); + + + + + /** + * User or moderator rejects this request + * @param invitationId + * @param reason + */ + public Invitation reject(String invitationId, String reason); + + + /** + * cancel this request + */ + public Invitation cancel (String invitationId); + + /** + * get an invitation from its invitation id + * + * @param invitationId; + */ + public Invitation getInvitation(String invitationId) ; + + /** + * list Invitations for a specific person + */ + public List listPendingInvitationsForInvitee(String invitee); + + /** + * list Invitations for a specific resource + * @param resourceType + * @param resourceName + */ + public List listPendingInvitationsForResource(Invitation.ResourceType resourceType, String resourceName); + + /** + * search invitation + * + * @param criteria + * @return the list of invitations + */ + public List searchInvitation(InvitationSearchCriteria criteria); + + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/ModeratedInvitation.java b/source/java/org/alfresco/service/cmr/invitation/ModeratedInvitation.java new file mode 100644 index 0000000000..e6956e2803 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/ModeratedInvitation.java @@ -0,0 +1,58 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + + +/** + * The moderated invitation request is a model object for who, needs to be added or removed + * from which resource with which attributes. + * + * Invitations are processed by the InvitationService + * + * @see org.alfresco.service.cmr.invitation.InvitationService + * + * @author mrogers + */ +public interface ModeratedInvitation extends Invitation +{ + /** + * Which resource to be invited to? + * @return the resource name. + */ + public String getResourceName(); + + /** + * Which role to be added with + * @return the roleName + */ + public String getRoleName(); + + /** + * The invitee comments - why does the invitee want access ? + * @return invitee comments + */ + public String getInviteeComments(); + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/NominatedInvitation.java b/source/java/org/alfresco/service/cmr/invitation/NominatedInvitation.java new file mode 100644 index 0000000000..7c198ffb20 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/NominatedInvitation.java @@ -0,0 +1,63 @@ +/* + * 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 recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.invitation; + +import java.util.Date; + + +/** + * The nominated invitation is a model object for who, needs to be added or removed + * from which resource with which attributes. + * + * Invitations are processed by the InvitationService + * + * @see org.alfresco.service.cmr.invitation.InvitationService + * + * @author mrogers + */ +public interface NominatedInvitation extends Invitation +{ + + public String getInviteeFirstName(); + + public String getInviteeLastName(); + + public String getInviteeEmail(); + + public String getResourceName(); + + public String getServerPath(); + + public String getAcceptUrl(); + + public String getRejectUrl(); + + public Date getSentInviteDate(); + + public String getTicket(); + + public String getRoleName(); + +} diff --git a/source/java/org/alfresco/service/cmr/invitation/package.html b/source/java/org/alfresco/service/cmr/invitation/package.html new file mode 100644 index 0000000000..aa06b0e6b1 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/invitation/package.html @@ -0,0 +1,2 @@ +The interface for the invitation service. + diff --git a/source/java/org/alfresco/service/cmr/site/SiteInfo.java b/source/java/org/alfresco/service/cmr/site/SiteInfo.java new file mode 100644 index 0000000000..b61c7d4b72 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/site/SiteInfo.java @@ -0,0 +1,106 @@ +package org.alfresco.service.cmr.site; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public interface SiteInfo +{ + /** + * Get the site node reference + * + * @return NodeRef site node reference, null if not set + */ + public abstract NodeRef getNodeRef(); + + /** + * Get the site preset + * + * @return String site preset + */ + public abstract String getSitePreset(); + + /** + * Get the short name + * + * @return String short name + */ + public abstract String getShortName(); + + /** + * Get the title + * + * @return String site title + */ + public abstract String getTitle(); + + /** + * Set the title + * + * @param title site title + */ + public abstract void setTitle(String title); + + /** + * Get the description + * + * @return String site description + */ + public abstract String getDescription(); + + /** + * Set the description + * + * @param description site description + */ + public abstract void setDescription(String description); + + /** + * Sets whether this site is public or not. If true the visibility is set to "public", if false + * the visibility is set to "private" + * + * @param isPublic true public, false private + * @deprecated as of version 3.2, replaced by {@link #setVisibility(SiteVisibility)} + */ + public abstract void setIsPublic(boolean isPublic); + + /** + * Indicates whether the site is public. + * + * @return boolean true if public, false either private or moderated + * @deprecated as of version 3.2, replaced by {@link #getVisibility()} + */ + public abstract boolean getIsPublic(); + + /** + * Get the sites visibility + * + * @return SiteVisibility site visibility + */ + public abstract SiteVisibility getVisibility(); + + /** + * Set the sites visibility + * + * @param visibility site visibility + */ + public abstract void setVisibility(SiteVisibility visibility); + + /** + * Get the custom property values + * + * @return Map map of custom property names and values + */ + public abstract Map getCustomProperties(); + + /** + * Get the value of a custom property + * + * @param name name of custom property + * @return Serializable value of the property, null if not set or doesn't exist + */ + public abstract Serializable getCustomProperty(QName name); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java similarity index 64% rename from source/java/org/alfresco/repo/site/SiteService.java rename to source/java/org/alfresco/service/cmr/site/SiteService.java index 74fc193cb4..77664bb6a1 100644 --- a/source/java/org/alfresco/repo/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -1,4 +1,4 @@ -package org.alfresco.repo.site; +package org.alfresco.service.cmr.site; import java.io.Serializable; import java.util.List; @@ -23,11 +23,24 @@ public interface SiteService * @param shortName site short name, must be unique * @param title site title * @param description site description - * @param isPublic whether the site is public or not + * @param isPublic whether the site is public or not (true = public, false = private) * @return SiteInfo information about the created site + * @deprecated since version 3.2, replaced by {@link #createSite(String, String, String, String, SiteVisibility)} */ SiteInfo createSite(String sitePreset, String shortName, String title, String description, boolean isPublic); + /** + * Create a new site. + * + * @param sitePreset site preset name + * @param shortName site short name, must be unique + * @param title site title + * @param description site description + * @param visibility site visibility (public|moderated|private) + * @return SiteInfo information about the created site + */ + SiteInfo createSite(String sitePreset, String shortName, String title, String description, SiteVisibility visibility); + /** * List the available sites. This list can optionally be filtered by site name and/or site preset. * @@ -58,7 +71,7 @@ public interface SiteService /** * Update the site information. *

- * Note that the shortname and sitepreset of a site can not be updated once the site has been created. + * Note that the short name and site preset of a site can not be updated once the site has been created. * * @param siteInfo site information */ @@ -72,51 +85,63 @@ public interface SiteService void deleteSite(String shortName); /** - * List the memebers of the site. + * List the members of the site. This includes both users and groups. *

* Name and role filters are optional and if not specified all the members of the site are returned. * * @param shortName site short name * @param nameFilter name filter * @param roleFilter role filter - * @return Map the username and their role + * @return Map the authority name and their role */ Map listMembers(String shortName, String nameFilter, String roleFilter); /** - * Gets the role of the specified user + * List the members of the site. This includes both users and groups if collapseGroups is set to false, otherwise all + * groups that are members are collapsed into their component users and listed. + * + * @param shortName site short name + * @param nameFilter name filter + * @param roleFilter role filter + * @param collapseGroups true if collapse member groups into user list, false otherwise + * @return Map the authority name and their role + */ + Map listMembers(String shortName, String nameFilter, String roleFilter, boolean collapseGroups); + + /** + * Gets the role of the specified user. * * @param shortName site short name - * @param userName user name + * @param authorityName authority name * @return String site role, null if none */ - String getMembersRole(String shortName, String userName); + String getMembersRole(String shortName, String authorityName); /** - * Inidiactes whether a user is a member of a site or not + * Indicates whether an authority is a member of a site or not * * @param shortName site short name - * @param userName user name - * @return boolean true if the user is a member of the site, false otherwise + * @param authorityName authority name + * @return boolean true if the authority is a member of the site, false otherwise */ - boolean isMember(String shortName, String userName); + boolean isMember(String shortName, String authorityName); /** - * Sets the role of a user withint a site + * Sets the role of an authority within a site * * @param shortName site short name - * @param userName user name + * @param authorityName authority name * @param role site role */ - void setMembership(String shortName, String userName, String role); + void setMembership(String shortName, String authorityName, String role); /** - * Clears a users role within a site + * Clears an authorities role within a site * * @param shortName site short name - * @param userName user name + * @param authorityName authority name */ - void removeMembership(String shortName, String userName); + void removeMembership(String shortName, String authorityName); /** * Creates a container for a component is a site of the given container type (must be a sub-type of st:siteContainer) @@ -157,7 +182,7 @@ public interface SiteService /** * Gets a list of all the currently available roles that a user can perform on a site * - * @return List list of availble roles + * @return List list of available roles */ List getSiteRoles(); diff --git a/source/java/org/alfresco/service/cmr/site/SiteVisibility.java b/source/java/org/alfresco/service/cmr/site/SiteVisibility.java new file mode 100644 index 0000000000..5335547738 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/site/SiteVisibility.java @@ -0,0 +1,16 @@ +/** + * + */ +package org.alfresco.service.cmr.site; + +/** + * Enumeration representing the different site visibilities. + * + * @author Roy Wetherall + */ +public enum SiteVisibility +{ + PUBLIC, // Public site. Visible and accessible by all + MODERATED, // Moderated site. Visible to all, but only accessible via moderated invitation. + PRIVATE // Private site. Visible and accessible only to members of the site. +}