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    }