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