001package com.box.sdk; 002 003import java.net.URL; 004import java.text.ParseException; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Date; 008 009import com.eclipsesource.json.JsonArray; 010import com.eclipsesource.json.JsonObject; 011import com.eclipsesource.json.JsonValue; 012 013/** 014 * Represents a collaboration between a user and another user or group. Collaborations are Box's equivalent of access 015 * control lists. They can be used to set and apply permissions for users or groups to folders. 016 * 017 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 018 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 019 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p> 020 */ 021public class BoxCollaboration extends BoxResource { 022 private static final URLTemplate COLLABORATIONS_URL_TEMPLATE = new URLTemplate("collaborations"); 023 private static final URLTemplate PENDING_COLLABORATIONS_URL = new URLTemplate("collaborations?status=pending"); 024 private static final URLTemplate COLLABORATION_URL_TEMPLATE = new URLTemplate("collaborations/%s"); 025 026 /** 027 * Constructs a BoxCollaboration for a collaboration with a given ID. 028 * @param api the API connection to be used by the collaboration. 029 * @param id the ID of the collaboration. 030 */ 031 public BoxCollaboration(BoxAPIConnection api, String id) { 032 super(api, id); 033 } 034 035 /** 036 * Gets all pending collaboration invites for the current user. 037 * @param api the API connection to use. 038 * @return a collection of pending collaboration infos. 039 */ 040 public static Collection<Info> getPendingCollaborations(BoxAPIConnection api) { 041 URL url = PENDING_COLLABORATIONS_URL.build(api.getBaseURL()); 042 043 BoxAPIRequest request = new BoxAPIRequest(api, url, "GET"); 044 BoxJSONResponse response = (BoxJSONResponse) request.send(); 045 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 046 047 int entriesCount = responseJSON.get("total_count").asInt(); 048 Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount); 049 JsonArray entries = responseJSON.get("entries").asArray(); 050 for (JsonValue entry : entries) { 051 JsonObject entryObject = entry.asObject(); 052 BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString()); 053 BoxCollaboration.Info info = collaboration.new Info(entryObject); 054 collaborations.add(info); 055 } 056 057 return collaborations; 058 } 059 060 /** 061 * Gets information about this collaboration. 062 * @return info about this collaboration. 063 */ 064 public Info getInfo() { 065 BoxAPIConnection api = this.getAPI(); 066 URL url = COLLABORATIONS_URL_TEMPLATE.build(api.getBaseURL()); 067 068 BoxAPIRequest request = new BoxAPIRequest(api, url, "GET"); 069 BoxJSONResponse response = (BoxJSONResponse) request.send(); 070 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 071 return new Info(jsonObject); 072 } 073 074 /** 075 * Updates the information about this collaboration with any info fields that have been modified locally. 076 * @param info the updated info. 077 */ 078 public void updateInfo(Info info) { 079 BoxAPIConnection api = this.getAPI(); 080 URL url = COLLABORATION_URL_TEMPLATE.build(api.getBaseURL(), this.getID()); 081 082 BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT"); 083 request.setBody(info.getPendingChanges()); 084 BoxJSONResponse response = (BoxJSONResponse) request.send(); 085 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 086 info.update(jsonObject); 087 } 088 089 /** 090 * Deletes this collaboration. 091 */ 092 public void delete() { 093 BoxAPIConnection api = this.getAPI(); 094 URL url = COLLABORATION_URL_TEMPLATE.build(api.getBaseURL(), this.getID()); 095 096 BoxAPIRequest request = new BoxAPIRequest(api, url, "DELETE"); 097 BoxAPIResponse response = request.send(); 098 response.disconnect(); 099 } 100 101 /** 102 * Contains information about a BoxCollaboration. 103 */ 104 public class Info extends BoxResource.Info { 105 private BoxUser.Info createdBy; 106 private Date createdAt; 107 private Date modifiedAt; 108 private Date expiresAt; 109 private Status status; 110 private BoxCollaborator.Info accessibleBy; 111 private Role role; 112 private Date acknowledgedAt; 113 private BoxFolder.Info item; 114 115 /** 116 * Constructs an empty Info object. 117 */ 118 public Info() { 119 super(); 120 } 121 122 /** 123 * Constructs an Info object by parsing information from a JSON string. 124 * @param json the JSON string to parse. 125 */ 126 public Info(String json) { 127 super(json); 128 } 129 130 Info(JsonObject jsonObject) { 131 super(jsonObject); 132 } 133 134 /** 135 * Gets the user who created the collaboration. 136 * @return the user who created the collaboration. 137 */ 138 public BoxUser.Info getCreatedBy() { 139 return this.createdBy; 140 } 141 142 /** 143 * Gets the time the collaboration was created. 144 * @return the time the collaboration was created. 145 */ 146 public Date getCreatedAt() { 147 return this.createdAt; 148 } 149 150 /** 151 * Gets the time the collaboration was last modified. 152 * @return the time the collaboration was last modified. 153 */ 154 public Date getModifiedAt() { 155 return this.modifiedAt; 156 } 157 158 /** 159 * Gets the time the collaboration will expire. 160 * @return the time the collaboration will expire. 161 */ 162 public Date getExpiresAt() { 163 return this.expiresAt; 164 } 165 166 /** 167 * Gets the status of the collaboration. 168 * @return the status of the collaboration. 169 */ 170 public Status getStatus() { 171 return this.status; 172 } 173 174 /** 175 * Sets the status of the collaboration in order to accept or reject the collaboration if it's pending. 176 * @param status the new status of the collaboration. 177 */ 178 public void setStatus(Status status) { 179 this.status = status; 180 this.addPendingChange("status", status.name().toLowerCase()); 181 } 182 183 /** 184 * Gets the collaborator who this collaboration applies to. 185 * @return the collaborator who this collaboration applies to. 186 */ 187 public BoxCollaborator.Info getAccessibleBy() { 188 return this.accessibleBy; 189 } 190 191 /** 192 * Gets the level of access the collaborator has. 193 * @return the level of access the collaborator has. 194 */ 195 public Role getRole() { 196 return this.role; 197 } 198 199 /** 200 * Sets the level of access the collaborator has. 201 * @param role the new level of access to give the collaborator. 202 */ 203 public void setRole(Role role) { 204 this.role = role; 205 this.addPendingChange("role", role.toJSONString()); 206 } 207 208 /** 209 * Gets the time the collaboration's status was changed. 210 * @return the time the collaboration's status was changed. 211 */ 212 public Date getAcknowledgedAt() { 213 return this.acknowledgedAt; 214 } 215 216 /** 217 * Gets the folder the collaboration is related to. 218 * @return the folder the collaboration is related to. 219 */ 220 public BoxFolder.Info getItem() { 221 return this.item; 222 } 223 224 @Override 225 public BoxCollaboration getResource() { 226 return BoxCollaboration.this; 227 } 228 229 @Override 230 protected void parseJSONMember(JsonObject.Member member) { 231 super.parseJSONMember(member); 232 233 String memberName = member.getName(); 234 JsonValue value = member.getValue(); 235 try { 236 if (memberName.equals("created_by")) { 237 JsonObject userJSON = value.asObject(); 238 if (this.createdBy == null) { 239 String userID = userJSON.get("id").asString(); 240 BoxUser user = new BoxUser(getAPI(), userID); 241 this.createdBy = user.new Info(userJSON); 242 } else { 243 this.createdBy.update(userJSON); 244 } 245 246 } else if (memberName.equals("created_at")) { 247 this.createdAt = BoxDateFormat.parse(value.asString()); 248 249 } else if (memberName.equals("modified_at")) { 250 this.modifiedAt = BoxDateFormat.parse(value.asString()); 251 252 } else if (memberName.equals("expires_at")) { 253 this.expiresAt = BoxDateFormat.parse(value.asString()); 254 255 } else if (memberName.equals("status")) { 256 String statusString = value.asString().toUpperCase(); 257 this.status = Status.valueOf(statusString); 258 259 } else if (memberName.equals("accessible_by")) { 260 JsonObject accessibleByJSON = value.asObject(); 261 if (this.accessibleBy == null) { 262 this.accessibleBy = this.parseAccessibleBy(accessibleByJSON); 263 } else { 264 this.updateAccessibleBy(accessibleByJSON); 265 } 266 } else if (memberName.equals("role")) { 267 this.role = Role.fromJSONString(value.asString()); 268 269 } else if (memberName.equals("acknowledged_at")) { 270 this.acknowledgedAt = BoxDateFormat.parse(value.asString()); 271 272 } else if (memberName.equals("item")) { 273 JsonObject folderJSON = value.asObject(); 274 if (this.item == null) { 275 String folderID = folderJSON.get("id").asString(); 276 BoxFolder folder = new BoxFolder(getAPI(), folderID); 277 this.item = folder.new Info(folderJSON); 278 } else { 279 this.item.update(folderJSON); 280 } 281 } 282 } catch (ParseException e) { 283 assert false : "A ParseException indicates a bug in the SDK."; 284 } 285 } 286 287 private void updateAccessibleBy(JsonObject json) { 288 String type = json.get("type").asString(); 289 if ((type.equals("user") && this.accessibleBy instanceof BoxUser.Info) 290 || (type.equals("group") && this.accessibleBy instanceof BoxGroup.Info)) { 291 292 this.accessibleBy.update(json); 293 } else { 294 this.accessibleBy = this.parseAccessibleBy(json); 295 } 296 } 297 298 private BoxCollaborator.Info parseAccessibleBy(JsonObject json) { 299 String id = json.get("id").asString(); 300 String type = json.get("type").asString(); 301 BoxCollaborator.Info parsedInfo = null; 302 if (type.equals("user")) { 303 BoxUser user = new BoxUser(getAPI(), id); 304 parsedInfo = user.new Info(json); 305 } else if (type.equals("group")) { 306 BoxGroup group = new BoxGroup(getAPI(), id); 307 parsedInfo = group.new Info(json); 308 } 309 310 return parsedInfo; 311 } 312 } 313 314 /** 315 * Enumerates the possible statuses that a collaboration can have. 316 */ 317 public enum Status { 318 /** 319 * The collaboration has been accepted. 320 */ 321 ACCEPTED, 322 323 /** 324 * The collaboration is waiting to be accepted or rejected. 325 */ 326 PENDING, 327 328 /** 329 * The collaboration has been rejected. 330 */ 331 REJECTED; 332 } 333 334 /** 335 * Enumerates the possible access levels that a collaborator can have. 336 */ 337 public enum Role { 338 /** 339 * An Editor has full read/write access to a folder. Once invited to a folder, they will be able to view, 340 * download, upload, edit, delete, copy, move, rename, generate shared links, make comments, assign tasks, 341 * create tags, and invite/remove collaborators. They will not be able to delete or move root level folders. 342 */ 343 EDITOR ("editor"), 344 345 /** 346 * The viewer role has full read access to a folder. Once invited to a folder, they will be able to preview, 347 * download, make comments, and generate shared links. They will not be able to add tags, invite new 348 * collaborators, upload, edit, or delete items in the folder. 349 */ 350 VIEWER ("viewer"), 351 352 /** 353 * The previewer role has limited read access to a folder. They will only be able to preview the items in the 354 * folder using the integrated content viewer. They will not be able to share, upload, edit, or delete any 355 * content. This role is only available to enterprise accounts. 356 */ 357 PREVIEWER ("previewer"), 358 359 /** 360 * The uploader has limited write access to a folder. They will only be able to upload and see the names of the 361 * items in a folder. They will not able to download or view any content. This role is only available to 362 * enterprise accounts. 363 */ 364 UPLOADER ("uploader"), 365 366 /** 367 * The previewer-uploader role is a combination of previewer and uploader. A user with this access level will be 368 * able to preview files using the integrated content viewer as well as upload items into the folder. They will 369 * not be able to download, edit, or share, items in the folder. This role is only available to enterprise 370 * accounts. 371 */ 372 PREVIEWER_UPLOADER ("previewer uploader"), 373 374 /** 375 * The viewer-uploader role is a combination of viewer and uploader. A viewer-uploader has full read access to a 376 * folder and limited write access. They are able to preview, download, add comments, generate shared links, and 377 * upload content to the folder. They will not be able to add tags, invite new collaborators, edit, or delete 378 * items in the folder. This role is only available to enterprise accounts. 379 */ 380 VIEWER_UPLOADER ("viewer uploader"), 381 382 /** 383 * The co-owner role has all of the functional read/write access that an editor does. This permission level has 384 * the added ability of being able to manage users in the folder. A co-owner can add new collaborators, change 385 * access levels of existing collaborators, and remove collaborators. However, they will not be able to 386 * manipulate the owner of the folder or transfer ownership to another user. This role is only available to 387 * enterprise accounts. 388 */ 389 CO_OWNER ("co-owner"), 390 391 /** 392 * The owner role has all of the functional capabilities of a co-owner. However, they will be able to manipulate 393 * the owner of the folder or transfer ownership to another user. This role is only available to enterprise 394 * accounts. 395 */ 396 OWNER ("owner"); 397 398 private final String jsonValue; 399 400 private Role(String jsonValue) { 401 this.jsonValue = jsonValue; 402 } 403 404 static Role fromJSONString(String jsonValue) { 405 if (jsonValue.equals("editor")) { 406 return EDITOR; 407 } else if (jsonValue.equals("viewer")) { 408 return VIEWER; 409 } else if (jsonValue.equals("previewer")) { 410 return PREVIEWER; 411 } else if (jsonValue.equals("uploader")) { 412 return UPLOADER; 413 } else if (jsonValue.equals("previewer uploader")) { 414 return PREVIEWER_UPLOADER; 415 } else if (jsonValue.equals("viewer uploader")) { 416 return VIEWER_UPLOADER; 417 } else if (jsonValue.equals("co-owner")) { 418 return CO_OWNER; 419 } else if (jsonValue.equals("owner")) { 420 return OWNER; 421 } else { 422 throw new IllegalArgumentException("The provided JSON value isn't a valid Role."); 423 } 424 } 425 426 String toJSONString() { 427 return this.jsonValue; 428 } 429 } 430}