001 package org.hackystat.sensorbase.resource.projects; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 006 import org.hackystat.sensorbase.resource.projects.jaxb.Invitations; 007 import org.hackystat.sensorbase.resource.projects.jaxb.Members; 008 import org.hackystat.sensorbase.resource.sensorbase.SensorBaseResource; 009 import org.restlet.Context; 010 import org.restlet.data.Request; 011 import org.restlet.data.Response; 012 import org.restlet.data.Status; 013 import org.restlet.resource.Representation; 014 import org.restlet.resource.Variant; 015 016 /** 017 * The resource for processing POST host/projects/{email}/{projectname}/invitation/{rsvp}. 018 * 019 * @author Philip Johnson 020 */ 021 public class UserProjectInvitationResource extends SensorBaseResource { 022 023 /** To be retrieved from the URL; should be "accept" or "decline". */ 024 private String rsvp; 025 /** The accept string. */ 026 private static final String ACCEPT = "accept"; 027 /** The decline string. */ 028 private static final String DECLINE = "decline"; 029 030 /** 031 * Provides the following representational variants: TEXT_XML. 032 * @param context The context. 033 * @param request The request object. 034 * @param response The response object. 035 */ 036 public UserProjectInvitationResource(Context context, Request request, Response response) { 037 super(context, request, response); 038 this.rsvp = (String) request.getAttributes().get("rsvp"); 039 } 040 041 /** 042 * Indicate the GET method is not supported. 043 * @return False. 044 */ 045 @Override 046 public boolean allowGet() { 047 return false; 048 } 049 050 /** 051 * Returns nothing since GET is not supported. 052 * 053 * @param variant The representational variant requested, or null if conditions are violated. 054 * @return The representation. 055 */ 056 @Override 057 public Representation represent(Variant variant) { 058 return null; 059 } 060 061 /** 062 * Indicate the POST method is supported. 063 * @return True. 064 */ 065 @Override 066 public boolean allowPost() { 067 return true; 068 } 069 070 /** 071 * Implement the POST method that processes a membership invitation RSVP. 072 * <ul> 073 * <li> UriUser must be a defined user, and user/project must be a defined project. 074 * <li> The rsvp part of the URL must be either "accept" or "decline". 075 * <li> The authorized user must be either a member or an invitee. 076 * <li> Cannot accept or decline the Default project. 077 * <li> Owner cannot accept or decline. 078 * <li> If accept, then make sure not in Invitations and make sure on Members. 079 * <li> If decline, then make sure not in either Invitations and Members. 080 * </ul> 081 * @param entity The XML representation of the new Project. 082 */ 083 @Override 084 public void acceptRepresentation(Representation entity) { 085 try { 086 if (!validateUriUserIsUser() || 087 !validateUriProjectName()) { 088 return; 089 } 090 091 // Error if rsvp is not "accept" or "decline". 092 if (!ACCEPT.equals(this.rsvp) && !DECLINE.equals(this.rsvp)) { 093 setStatusMiscError("URL must end with 'accept' or 'decline'"); 094 return; 095 } 096 // Get the (possibly empty) list of members and invitees. 097 List<String> members = ((project.getMembers() == null) ? new ArrayList<String>() 098 : project.getMembers().getMember()); 099 List<String> invitees = ((project.getInvitations() == null) ? 100 new ArrayList<String>() : project.getInvitations().getInvitation()); 101 102 // Make sure that authorized user is either an invitee or a member. 103 if (!members.contains(this.authUser) && !invitees.contains(this.authUser)) { 104 setStatusMiscError(String.format("User %s is not a member or invitee of Project %s", 105 this.authUser, this.projectName)); 106 return; 107 } 108 109 // Cannot accept or decline the default project. 110 if (ProjectManager.DEFAULT_PROJECT_NAME.equals(this.projectName)) { 111 setStatusMiscError("Cannot accept or decline the default project."); 112 return; 113 } 114 115 // Owner cannot accept or decline their own project. 116 if (project.getOwner().equals(this.authUser)) { 117 setStatusMiscError("Owner cannot accept or decline their own project."); 118 return; 119 } 120 121 // Now update Project data structure. First make sure Members and Invitations exist. 122 if (project.getMembers() == null) { 123 project.setMembers(new Members()); 124 } 125 if (project.getInvitations() == null) { 126 project.setInvitations(new Invitations()); 127 } 128 129 // If accepting, make sure it's not in invitees, and make sure it is in members. 130 if (ACCEPT.equals(this.rsvp)) { 131 // Make sure authUser is no longer in invitees. 132 project.getInvitations().getInvitation().remove(this.authUser); 133 // Make sure authUser is present in members if not there already. 134 if (!project.getMembers().getMember().contains(this.authUser)) { 135 project.getMembers().getMember().add(this.authUser); 136 } 137 } 138 // If declining, make sure it's nowhere. 139 if (DECLINE.equals(this.rsvp)) { 140 project.getInvitations().getInvitation().remove(this.authUser); 141 project.getMembers().getMember().remove(this.authUser); 142 } 143 144 // Now we add it to the Manager and return success. 145 super.projectManager.putProject(project); 146 getResponse().setStatus(Status.SUCCESS_OK); 147 } 148 catch (RuntimeException e) { 149 setStatusInternalError(e); 150 } 151 } 152 153 }