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