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