001package com.box.sdk; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005import java.io.InputStream; 006import java.io.OutputStream; 007import java.net.MalformedURLException; 008import java.net.URL; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.Collection; 012import java.util.Date; 013import java.util.EnumSet; 014import java.util.HashSet; 015import java.util.List; 016import java.util.Map; 017import java.util.Set; 018import java.util.concurrent.TimeUnit; 019 020import com.box.sdk.internal.utils.Parsers; 021import com.eclipsesource.json.JsonArray; 022import com.eclipsesource.json.JsonObject; 023import com.eclipsesource.json.JsonValue; 024 025 026/** 027 * Represents an individual file on Box. This class can be used to download a file's contents, upload new versions, and 028 * perform other common file operations (move, copy, delete, etc.). 029 * 030 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 031 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 032 * handling for errors related to the Box REST API, you should capture this exception explicitly. 033 */ 034@BoxResourceType("file") 035public class BoxFile extends BoxItem { 036 037 /** 038 * An array of all possible file fields that can be requested when calling {@link #getInfo()}. 039 */ 040 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", 041 "description", "size", "path_collection", "created_at", "modified_at", 042 "trashed_at", "purged_at", "content_created_at", "content_modified_at", 043 "created_by", "modified_by", "owned_by", "shared_link", "parent", 044 "item_status", "version_number", "comment_count", "permissions", "tags", 045 "lock", "extension", "is_package", "file_version", "collections", 046 "watermark_info", "metadata", "representations"}; 047 048 /** 049 * Used to specify what filetype to request for a file thumbnail. 050 */ 051 public enum ThumbnailFileType { 052 /** 053 * PNG image format. 054 */ 055 PNG, 056 057 /** 058 * JPG image format. 059 */ 060 JPG 061 } 062 063 /** 064 * File URL Template. 065 */ 066 public static final URLTemplate FILE_URL_TEMPLATE = new URLTemplate("files/%s"); 067 /** 068 * Content URL Template. 069 */ 070 public static final URLTemplate CONTENT_URL_TEMPLATE = new URLTemplate("files/%s/content"); 071 /** 072 * Versions URL Template. 073 */ 074 public static final URLTemplate VERSIONS_URL_TEMPLATE = new URLTemplate("files/%s/versions"); 075 /** 076 * Copy URL Template. 077 */ 078 public static final URLTemplate COPY_URL_TEMPLATE = new URLTemplate("files/%s/copy"); 079 /** 080 * Add Comment URL Template. 081 */ 082 public static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments"); 083 /** 084 * Get Comments URL Template. 085 */ 086 public static final URLTemplate GET_COMMENTS_URL_TEMPLATE = new URLTemplate("files/%s/comments"); 087 /** 088 * Metadata URL Template. 089 */ 090 public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("files/%s/metadata/%s/%s"); 091 /** 092 * Add Task URL Template. 093 */ 094 public static final URLTemplate ADD_TASK_URL_TEMPLATE = new URLTemplate("tasks"); 095 /** 096 * Get Tasks URL Template. 097 */ 098 public static final URLTemplate GET_TASKS_URL_TEMPLATE = new URLTemplate("files/%s/tasks"); 099 /** 100 * Get Thumbnail PNG Template. 101 */ 102 public static final URLTemplate GET_THUMBNAIL_PNG_TEMPLATE = new URLTemplate("files/%s/thumbnail.png"); 103 /** 104 * Get Thumbnail JPG Template. 105 */ 106 public static final URLTemplate GET_THUMBNAIL_JPG_TEMPLATE = new URLTemplate("files/%s/thumbnail.jpg"); 107 /** 108 * Upload Session URL Template. 109 */ 110 public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/%s/upload_sessions"); 111 /** 112 * Upload Session Status URL Template. 113 */ 114 public static final URLTemplate UPLOAD_SESSION_STATUS_URL_TEMPLATE = new URLTemplate( 115 "files/upload_sessions/%s/status"); 116 /** 117 * Abort Upload Session URL Template. 118 */ 119 public static final URLTemplate ABORT_UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions/%s"); 120 /** 121 * Add Collaborations URL Template. 122 */ 123 public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations"); 124 /** 125 * Get All File Collaborations URL Template. 126 */ 127 public static final URLTemplate GET_ALL_FILE_COLLABORATIONS_URL = new URLTemplate("files/%s/collaborations"); 128 private static final int BUFFER_SIZE = 8192; 129 private static final int GET_COLLABORATORS_PAGE_SIZE = 1000; 130 131 /** 132 * Constructs a BoxFile for a file with a given ID. 133 * 134 * @param api the API connection to be used by the file. 135 * @param id the ID of the file. 136 */ 137 public BoxFile(BoxAPIConnection api, String id) { 138 super(api, id); 139 } 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override 145 protected URL getItemURL() { 146 return FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 147 } 148 149 @Override 150 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 151 BoxSharedLink.Permissions permissions) { 152 153 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions); 154 Info info = new Info(); 155 info.setSharedLink(sharedLink); 156 157 this.updateInfo(info); 158 return info.getSharedLink(); 159 } 160 161 /** 162 * Adds new {@link BoxWebHook} to this {@link BoxFile}. 163 * 164 * @param address {@link BoxWebHook.Info#getAddress()} 165 * @param triggers {@link BoxWebHook.Info#getTriggers()} 166 * @return created {@link BoxWebHook.Info} 167 */ 168 public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) { 169 return BoxWebHook.create(this, address, triggers); 170 } 171 172 /** 173 * Adds a comment to this file. The message can contain @mentions by using the string @[userid:username] anywhere 174 * within the message, where userid and username are the ID and username of the person being mentioned. 175 * 176 * @param message the comment's message. 177 * @return information about the newly added comment. 178 * @see <a href="https://developers.box.com/docs/#comments-add-a-comment-to-an-item">the tagged_message field 179 * for including @mentions.</a> 180 */ 181 public BoxComment.Info addComment(String message) { 182 JsonObject itemJSON = new JsonObject(); 183 itemJSON.add("type", "file"); 184 itemJSON.add("id", this.getID()); 185 186 JsonObject requestJSON = new JsonObject(); 187 requestJSON.add("item", itemJSON); 188 if (BoxComment.messageContainsMention(message)) { 189 requestJSON.add("tagged_message", message); 190 } else { 191 requestJSON.add("message", message); 192 } 193 194 URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 195 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 196 request.setBody(requestJSON.toString()); 197 BoxJSONResponse response = (BoxJSONResponse) request.send(); 198 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 199 200 BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString()); 201 return addedComment.new Info(responseJSON); 202 } 203 204 /** 205 * Adds a new task to this file. The task can have an optional message to include, and a due date. 206 * 207 * @param action the action the task assignee will be prompted to do. 208 * @param message an optional message to include with the task. 209 * @param dueAt the day at which this task is due. 210 * @return information about the newly added task. 211 */ 212 public BoxTask.Info addTask(BoxTask.Action action, String message, Date dueAt) { 213 JsonObject itemJSON = new JsonObject(); 214 itemJSON.add("type", "file"); 215 itemJSON.add("id", this.getID()); 216 217 JsonObject requestJSON = new JsonObject(); 218 requestJSON.add("item", itemJSON); 219 requestJSON.add("action", action.toJSONString()); 220 221 if (message != null && !message.isEmpty()) { 222 requestJSON.add("message", message); 223 } 224 225 if (dueAt != null) { 226 requestJSON.add("due_at", BoxDateFormat.format(dueAt)); 227 } 228 229 URL url = ADD_TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 230 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 231 request.setBody(requestJSON.toString()); 232 BoxJSONResponse response = (BoxJSONResponse) request.send(); 233 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 234 235 BoxTask addedTask = new BoxTask(this.getAPI(), responseJSON.get("id").asString()); 236 return addedTask.new Info(responseJSON); 237 } 238 239 /** 240 * Gets an expiring URL for downloading a file directly from Box. This can be user, 241 * for example, for sending as a redirect to a browser to cause the browser 242 * to download the file directly from Box. 243 * 244 * @return the temporary download URL 245 */ 246 public URL getDownloadURL() { 247 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 248 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 249 request.setFollowRedirects(false); 250 251 BoxRedirectResponse response = (BoxRedirectResponse) request.send(); 252 253 return response.getRedirectURL(); 254 } 255 256 /** 257 * Downloads the contents of this file to a given OutputStream. 258 * 259 * @param output the stream to where the file will be written. 260 */ 261 public void download(OutputStream output) { 262 this.download(output, null); 263 } 264 265 /** 266 * Downloads the contents of this file to a given OutputStream while reporting the progress to a ProgressListener. 267 * 268 * @param output the stream to where the file will be written. 269 * @param listener a listener for monitoring the download's progress. 270 */ 271 public void download(OutputStream output, ProgressListener listener) { 272 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 273 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 274 BoxAPIResponse response = request.send(); 275 InputStream input = response.getBody(listener); 276 277 byte[] buffer = new byte[BUFFER_SIZE]; 278 try { 279 int n = input.read(buffer); 280 while (n != -1) { 281 output.write(buffer, 0, n); 282 n = input.read(buffer); 283 } 284 } catch (IOException e) { 285 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 286 } finally { 287 response.disconnect(); 288 } 289 } 290 291 /** 292 * Downloads a part of this file's contents, starting at specified byte offset. 293 * 294 * @param output the stream to where the file will be written. 295 * @param offset the byte offset at which to start the download. 296 */ 297 public void downloadRange(OutputStream output, long offset) { 298 this.downloadRange(output, offset, -1); 299 } 300 301 /** 302 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd. 303 * 304 * @param output the stream to where the file will be written. 305 * @param rangeStart the byte offset at which to start the download. 306 * @param rangeEnd the byte offset at which to stop the download. 307 */ 308 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd) { 309 this.downloadRange(output, rangeStart, rangeEnd, null); 310 } 311 312 /** 313 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd, while reporting the 314 * progress to a ProgressListener. 315 * 316 * @param output the stream to where the file will be written. 317 * @param rangeStart the byte offset at which to start the download. 318 * @param rangeEnd the byte offset at which to stop the download. 319 * @param listener a listener for monitoring the download's progress. 320 */ 321 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd, ProgressListener listener) { 322 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 323 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 324 if (rangeEnd > 0) { 325 request.addHeader("Range", String.format("bytes=%s-%s", Long.toString(rangeStart), 326 Long.toString(rangeEnd))); 327 } else { 328 request.addHeader("Range", String.format("bytes=%s-", Long.toString(rangeStart))); 329 } 330 331 BoxAPIResponse response = request.send(); 332 InputStream input = response.getBody(listener); 333 334 byte[] buffer = new byte[BUFFER_SIZE]; 335 try { 336 int n = input.read(buffer); 337 while (n != -1) { 338 output.write(buffer, 0, n); 339 n = input.read(buffer); 340 } 341 } catch (IOException e) { 342 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 343 } finally { 344 response.disconnect(); 345 } 346 } 347 348 @Override 349 public BoxFile.Info copy(BoxFolder destination) { 350 return this.copy(destination, null); 351 } 352 353 @Override 354 public BoxFile.Info copy(BoxFolder destination, String newName) { 355 URL url = COPY_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 356 357 JsonObject parent = new JsonObject(); 358 parent.add("id", destination.getID()); 359 360 JsonObject copyInfo = new JsonObject(); 361 copyInfo.add("parent", parent); 362 if (newName != null) { 363 copyInfo.add("name", newName); 364 } 365 366 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 367 request.setBody(copyInfo.toString()); 368 BoxJSONResponse response = (BoxJSONResponse) request.send(); 369 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 370 BoxFile copiedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 371 return copiedFile.new Info(responseJSON); 372 } 373 374 /** 375 * Deletes this file by moving it to the trash. 376 */ 377 public void delete() { 378 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 379 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 380 BoxAPIResponse response = request.send(); 381 response.disconnect(); 382 } 383 384 @Override 385 public BoxItem.Info move(BoxFolder destination) { 386 return this.move(destination, null); 387 } 388 389 @Override 390 public BoxItem.Info move(BoxFolder destination, String newName) { 391 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 392 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 393 394 JsonObject parent = new JsonObject(); 395 parent.add("id", destination.getID()); 396 397 JsonObject updateInfo = new JsonObject(); 398 updateInfo.add("parent", parent); 399 if (newName != null) { 400 updateInfo.add("name", newName); 401 } 402 403 request.setBody(updateInfo.toString()); 404 BoxJSONResponse response = (BoxJSONResponse) request.send(); 405 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 406 BoxFile movedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 407 return movedFile.new Info(responseJSON); 408 } 409 410 /** 411 * Renames this file. 412 * 413 * @param newName the new name of the file. 414 */ 415 public void rename(String newName) { 416 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 417 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 418 419 JsonObject updateInfo = new JsonObject(); 420 updateInfo.add("name", newName); 421 422 request.setBody(updateInfo.toString()); 423 BoxAPIResponse response = request.send(); 424 response.disconnect(); 425 } 426 427 @Override 428 public BoxFile.Info getInfo() { 429 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 430 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 431 BoxJSONResponse response = (BoxJSONResponse) request.send(); 432 return new Info(response.getJSON()); 433 } 434 435 @Override 436 public BoxFile.Info getInfo(String... fields) { 437 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 438 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 439 440 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 441 BoxJSONResponse response = (BoxJSONResponse) request.send(); 442 return new Info(response.getJSON()); 443 } 444 445 /** 446 * Gets information about this item including a specified set of representations. 447 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 448 * 449 * @param representationHints hints for representations to be retrieved 450 * @param fields the fields to retrieve. 451 * @return info about this item containing only the specified fields, including representations. 452 */ 453 public BoxFile.Info getInfoWithRepresentations(String representationHints, String... fields) { 454 if (representationHints.matches(Representation.X_REP_HINTS_PATTERN)) { 455 //Since the user intends to get representations, add it to fields, even if user has missed it 456 Set<String> fieldsSet = new HashSet<String>(Arrays.asList(fields)); 457 fieldsSet.add("representations"); 458 String queryString = new QueryStringBuilder().appendParam("fields", 459 fieldsSet.toArray(new String[fieldsSet.size()])).toString(); 460 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 461 462 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 463 request.addHeader("X-Rep-Hints", representationHints); 464 BoxJSONResponse response = (BoxJSONResponse) request.send(); 465 return new Info(response.getJSON()); 466 } else { 467 throw new BoxAPIException("Represention hints is not valid." 468 + " Refer documention on how to construct X-Rep-Hints Header"); 469 } 470 } 471 472 /** 473 * Updates the information about this file with any info fields that have been modified locally. 474 * 475 * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following 476 * code won't update any information (or even send a network request) since none of the info's fields were 477 * changed:</p> 478 * 479 * <pre>BoxFile file = new File(api, id); 480 * BoxFile.Info info = file.getInfo(); 481 * file.updateInfo(info);</pre> 482 * 483 * @param info the updated info. 484 */ 485 public void updateInfo(BoxFile.Info info) { 486 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 487 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 488 request.setBody(info.getPendingChanges()); 489 BoxJSONResponse response = (BoxJSONResponse) request.send(); 490 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 491 info.update(jsonObject); 492 } 493 494 /** 495 * Gets any previous versions of this file. Note that only users with premium accounts will be able to retrieve 496 * previous versions of their files. 497 * 498 * @return a list of previous file versions. 499 */ 500 public Collection<BoxFileVersion> getVersions() { 501 URL url = VERSIONS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 502 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 503 BoxJSONResponse response = (BoxJSONResponse) request.send(); 504 505 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 506 JsonArray entries = jsonObject.get("entries").asArray(); 507 Collection<BoxFileVersion> versions = new ArrayList<BoxFileVersion>(); 508 for (JsonValue entry : entries) { 509 versions.add(new BoxFileVersion(this.getAPI(), entry.asObject(), this.getID())); 510 } 511 512 return versions; 513 } 514 515 /** 516 * Checks if the file can be successfully uploaded by using the preflight check. 517 * 518 * @param name the name to give the uploaded file or null to use existing name. 519 * @param fileSize the size of the file used for account capacity calculations. 520 * @param parentID the ID of the parent folder that the new version is being uploaded to. 521 */ 522 public void canUploadVersion(String name, long fileSize, String parentID) { 523 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 524 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 525 526 JsonObject parent = new JsonObject(); 527 parent.add("id", parentID); 528 529 JsonObject preflightInfo = new JsonObject(); 530 preflightInfo.add("parent", parent); 531 if (name != null) { 532 preflightInfo.add("name", name); 533 } 534 535 preflightInfo.add("size", fileSize); 536 537 request.setBody(preflightInfo.toString()); 538 BoxAPIResponse response = request.send(); 539 response.disconnect(); 540 } 541 542 /** 543 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 544 * will be able to view and recover previous versions of the file. 545 * 546 * @param fileContent a stream containing the new file contents. 547 */ 548 public void uploadVersion(InputStream fileContent) { 549 this.uploadVersion(fileContent, null); 550 } 551 552 /** 553 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 554 * will be able to view and recover previous versions of the file. 555 * 556 * @param fileContent a stream containing the new file contents. 557 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 558 */ 559 public void uploadVersion(InputStream fileContent, String fileContentSHA1) { 560 this.uploadVersion(fileContent, fileContentSHA1, null); 561 } 562 563 /** 564 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 565 * will be able to view and recover previous versions of the file. 566 * 567 * @param fileContent a stream containing the new file contents. 568 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 569 * @param modified the date that the new version was modified. 570 */ 571 public void uploadVersion(InputStream fileContent, String fileContentSHA1, Date modified) { 572 this.uploadVersion(fileContent, fileContentSHA1, modified, 0, null); 573 } 574 575 /** 576 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 577 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 578 * of the file. 579 * 580 * @param fileContent a stream containing the new file contents. 581 * @param modified the date that the new version was modified. 582 * @param fileSize the size of the file used for determining the progress of the upload. 583 * @param listener a listener for monitoring the upload's progress. 584 */ 585 public void uploadVersion(InputStream fileContent, Date modified, long fileSize, ProgressListener listener) { 586 this.uploadVersion(fileContent, null, modified, fileSize, listener); 587 } 588 589 /** 590 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 591 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 592 * of the file. 593 * 594 * @param fileContent a stream containing the new file contents. 595 * @param fileContentSHA1 the SHA1 hash of the file contents. will be sent along in the Content-MD5 header 596 * @param modified the date that the new version was modified. 597 * @param fileSize the size of the file used for determining the progress of the upload. 598 * @param listener a listener for monitoring the upload's progress. 599 */ 600 public void uploadVersion(InputStream fileContent, String fileContentSHA1, Date modified, long fileSize, 601 ProgressListener listener) { 602 URL uploadURL = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 603 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 604 605 if (fileSize > 0) { 606 request.setFile(fileContent, "", fileSize); 607 } else { 608 request.setFile(fileContent, ""); 609 } 610 611 if (fileContentSHA1 != null) { 612 request.setContentSHA1(fileContentSHA1); 613 } 614 615 if (modified != null) { 616 request.putField("content_modified_at", modified); 617 } 618 619 BoxJSONResponse response; 620 if (listener == null) { 621 response = (BoxJSONResponse) request.send(); 622 } else { 623 response = (BoxJSONResponse) request.send(listener); 624 } 625 response.getJSON(); 626 } 627 628 /** 629 * Gets an expiring URL for creating an embedded preview session. The URL will expire after 60 seconds and the 630 * preview session will expire after 60 minutes. 631 * 632 * @return the expiring preview link 633 */ 634 public URL getPreviewLink() { 635 BoxFile.Info info = this.getInfo("expiring_embed_link"); 636 637 return info.getPreviewLink(); 638 } 639 640 641 /** 642 * Retrieves a thumbnail, or smaller image representation, of this file. Sizes of 32x32, 64x64, 128x128, 643 * and 256x256 can be returned in the .png format and sizes of 32x32, 94x94, 160x160, and 320x320 can be returned 644 * in the .jpg format. 645 * 646 * @param fileType either PNG of JPG 647 * @param minWidth minimum width 648 * @param minHeight minimum height 649 * @param maxWidth maximum width 650 * @param maxHeight maximum height 651 * @return the byte array of the thumbnail image 652 */ 653 public byte[] getThumbnail(ThumbnailFileType fileType, int minWidth, int minHeight, int maxWidth, int maxHeight) { 654 QueryStringBuilder builder = new QueryStringBuilder(); 655 builder.appendParam("min_width", minWidth); 656 builder.appendParam("min_height", minHeight); 657 builder.appendParam("max_width", maxWidth); 658 builder.appendParam("max_height", maxHeight); 659 660 URLTemplate template; 661 if (fileType == ThumbnailFileType.PNG) { 662 template = GET_THUMBNAIL_PNG_TEMPLATE; 663 } else if (fileType == ThumbnailFileType.JPG) { 664 template = GET_THUMBNAIL_JPG_TEMPLATE; 665 } else { 666 throw new BoxAPIException("Unsupported thumbnail file type"); 667 } 668 URL url = template.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 669 670 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 671 BoxAPIResponse response = request.send(); 672 673 ByteArrayOutputStream thumbOut = new ByteArrayOutputStream(); 674 InputStream body = response.getBody(); 675 byte[] buffer = new byte[BUFFER_SIZE]; 676 try { 677 int n = body.read(buffer); 678 while (n != -1) { 679 thumbOut.write(buffer, 0, n); 680 n = body.read(buffer); 681 } 682 } catch (IOException e) { 683 throw new BoxAPIException("Error reading thumbnail bytes from response body", e); 684 } finally { 685 response.disconnect(); 686 } 687 688 return thumbOut.toByteArray(); 689 } 690 691 /** 692 * Gets a list of any comments on this file. 693 * 694 * @return a list of comments on this file. 695 */ 696 public List<BoxComment.Info> getComments() { 697 URL url = GET_COMMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 698 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 699 BoxJSONResponse response = (BoxJSONResponse) request.send(); 700 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 701 702 int totalCount = responseJSON.get("total_count").asInt(); 703 List<BoxComment.Info> comments = new ArrayList<BoxComment.Info>(totalCount); 704 JsonArray entries = responseJSON.get("entries").asArray(); 705 for (JsonValue value : entries) { 706 JsonObject commentJSON = value.asObject(); 707 BoxComment comment = new BoxComment(this.getAPI(), commentJSON.get("id").asString()); 708 BoxComment.Info info = comment.new Info(commentJSON); 709 comments.add(info); 710 } 711 712 return comments; 713 } 714 715 /** 716 * Gets a list of any tasks on this file. 717 * 718 * @return a list of tasks on this file. 719 */ 720 public List<BoxTask.Info> getTasks() { 721 URL url = GET_TASKS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 722 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 723 BoxJSONResponse response = (BoxJSONResponse) request.send(); 724 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 725 726 int totalCount = responseJSON.get("total_count").asInt(); 727 List<BoxTask.Info> tasks = new ArrayList<BoxTask.Info>(totalCount); 728 JsonArray entries = responseJSON.get("entries").asArray(); 729 for (JsonValue value : entries) { 730 JsonObject taskJSON = value.asObject(); 731 BoxTask task = new BoxTask(this.getAPI(), taskJSON.get("id").asString()); 732 BoxTask.Info info = task.new Info(taskJSON); 733 tasks.add(info); 734 } 735 736 return tasks; 737 } 738 739 /** 740 * Creates metadata on this file in the global properties template. 741 * 742 * @param metadata The new metadata values. 743 * @return the metadata returned from the server. 744 */ 745 public Metadata createMetadata(Metadata metadata) { 746 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 747 } 748 749 /** 750 * Creates metadata on this file in the specified template type. 751 * 752 * @param typeName the metadata template type name. 753 * @param metadata the new metadata values. 754 * @return the metadata returned from the server. 755 */ 756 public Metadata createMetadata(String typeName, Metadata metadata) { 757 String scope = Metadata.scopeBasedOnType(typeName); 758 return this.createMetadata(typeName, scope, metadata); 759 } 760 761 /** 762 * Creates metadata on this file in the specified template type. 763 * 764 * @param typeName the metadata template type name. 765 * @param scope the metadata scope (global or enterprise). 766 * @param metadata the new metadata values. 767 * @return the metadata returned from the server. 768 */ 769 public Metadata createMetadata(String typeName, String scope, Metadata metadata) { 770 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 771 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST"); 772 request.addHeader("Content-Type", "application/json"); 773 request.setBody(metadata.toString()); 774 BoxJSONResponse response = (BoxJSONResponse) request.send(); 775 return new Metadata(JsonObject.readFrom(response.getJSON())); 776 } 777 778 /** 779 * Locks a file. 780 * 781 * @param expiresAt expiration date of the lock. 782 * @return the lock returned from the server. 783 */ 784 public BoxLock lock(Date expiresAt) { 785 return this.lock(expiresAt, false); 786 } 787 788 /** 789 * Locks a file. 790 * 791 * @param expiresAt expiration date of the lock. 792 * @param isDownloadPrevented is downloading of file prevented when locked. 793 * @return the lock returned from the server. 794 */ 795 public BoxLock lock(Date expiresAt, boolean isDownloadPrevented) { 796 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 797 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 798 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 799 800 JsonObject lockConfig = new JsonObject(); 801 lockConfig.add("type", "lock"); 802 lockConfig.add("expires_at", BoxDateFormat.format(expiresAt)); 803 lockConfig.add("is_download_prevented", isDownloadPrevented); 804 805 JsonObject requestJSON = new JsonObject(); 806 requestJSON.add("lock", lockConfig); 807 request.setBody(requestJSON.toString()); 808 809 BoxJSONResponse response = (BoxJSONResponse) request.send(); 810 811 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 812 JsonValue lockValue = responseJSON.get("lock"); 813 JsonObject lockJSON = JsonObject.readFrom(lockValue.toString()); 814 815 return new BoxLock(lockJSON, this.getAPI()); 816 } 817 818 /** 819 * Unlocks a file. 820 */ 821 public void unlock() { 822 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 823 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 824 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 825 826 JsonObject lockObject = new JsonObject(); 827 lockObject.add("lock", JsonObject.NULL); 828 829 request.setBody(lockObject.toString()); 830 request.send(); 831 } 832 833 /** 834 * Used to retrieve all metadata associated with the file. 835 * 836 * @param fields the optional fields to retrieve. 837 * @return An iterable of metadata instances associated with the file. 838 */ 839 public Iterable<Metadata> getAllMetadata(String... fields) { 840 return Metadata.getAllMetadata(this, fields); 841 } 842 843 /** 844 * Gets the file properties metadata. 845 * 846 * @return the metadata returned from the server. 847 */ 848 public Metadata getMetadata() { 849 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 850 } 851 852 /** 853 * Gets the file metadata of specified template type. 854 * 855 * @param typeName the metadata template type name. 856 * @return the metadata returned from the server. 857 */ 858 public Metadata getMetadata(String typeName) { 859 String scope = Metadata.scopeBasedOnType(typeName); 860 return this.getMetadata(typeName, scope); 861 } 862 863 /** 864 * Gets the file metadata of specified template type. 865 * 866 * @param typeName the metadata template type name. 867 * @param scope the metadata scope (global or enterprise). 868 * @return the metadata returned from the server. 869 */ 870 public Metadata getMetadata(String typeName, String scope) { 871 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 872 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 873 BoxJSONResponse response = (BoxJSONResponse) request.send(); 874 return new Metadata(JsonObject.readFrom(response.getJSON())); 875 } 876 877 /** 878 * Updates the file metadata. 879 * 880 * @param metadata the new metadata values. 881 * @return the metadata returned from the server. 882 */ 883 public Metadata updateMetadata(Metadata metadata) { 884 String scope; 885 if (metadata.getScope().equals(Metadata.GLOBAL_METADATA_SCOPE)) { 886 scope = Metadata.GLOBAL_METADATA_SCOPE; 887 } else { 888 scope = Metadata.ENTERPRISE_METADATA_SCOPE; 889 } 890 891 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), 892 scope, metadata.getTemplateName()); 893 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 894 request.addHeader("Content-Type", "application/json-patch+json"); 895 request.setBody(metadata.getPatch()); 896 BoxJSONResponse response = (BoxJSONResponse) request.send(); 897 return new Metadata(JsonObject.readFrom(response.getJSON())); 898 } 899 900 /** 901 * Deletes the file properties metadata. 902 */ 903 public void deleteMetadata() { 904 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 905 } 906 907 /** 908 * Deletes the file metadata of specified template type. 909 * 910 * @param typeName the metadata template type name. 911 */ 912 public void deleteMetadata(String typeName) { 913 String scope = Metadata.scopeBasedOnType(typeName); 914 this.deleteMetadata(typeName, scope); 915 } 916 917 /** 918 * Deletes the file metadata of specified template type. 919 * 920 * @param typeName the metadata template type name. 921 * @param scope the metadata scope (global or enterprise). 922 */ 923 public void deleteMetadata(String typeName, String scope) { 924 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 925 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 926 request.send(); 927 } 928 929 /** 930 * Used to retrieve the watermark for the file. 931 * If the file does not have a watermark applied to it, a 404 Not Found will be returned by API. 932 * 933 * @param fields the fields to retrieve. 934 * @return the watermark associated with the file. 935 */ 936 public BoxWatermark getWatermark(String... fields) { 937 return this.getWatermark(FILE_URL_TEMPLATE, fields); 938 } 939 940 /** 941 * Used to apply or update the watermark for the file. 942 * 943 * @return the watermark associated with the file. 944 */ 945 public BoxWatermark applyWatermark() { 946 return this.applyWatermark(FILE_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 947 } 948 949 /** 950 * Removes a watermark from the file. 951 * If the file did not have a watermark applied to it, a 404 Not Found will be returned by API. 952 */ 953 public void removeWatermark() { 954 this.removeWatermark(FILE_URL_TEMPLATE); 955 } 956 957 /** 958 * {@inheritDoc} 959 */ 960 @Override 961 public BoxFile.Info setCollections(BoxCollection... collections) { 962 JsonArray jsonArray = new JsonArray(); 963 for (BoxCollection collection : collections) { 964 JsonObject collectionJSON = new JsonObject(); 965 collectionJSON.add("id", collection.getID()); 966 jsonArray.add(collectionJSON); 967 } 968 JsonObject infoJSON = new JsonObject(); 969 infoJSON.add("collections", jsonArray); 970 971 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 972 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 973 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 974 request.setBody(infoJSON.toString()); 975 BoxJSONResponse response = (BoxJSONResponse) request.send(); 976 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 977 return new Info(jsonObject); 978 } 979 980 /** 981 * Creates an upload session to create a new version of a file in chunks. 982 * This will first verify that the version can be created and then open a session for uploading pieces of the file. 983 * @param fileSize the size of the file that will be uploaded. 984 * @return the created upload session instance. 985 */ 986 public BoxFileUploadSession.Info createUploadSession(long fileSize) { 987 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 988 989 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 990 request.addHeader("Content-Type", "application/json"); 991 992 JsonObject body = new JsonObject(); 993 body.add("file_size", fileSize); 994 request.setBody(body.toString()); 995 996 BoxJSONResponse response = (BoxJSONResponse) request.send(); 997 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 998 999 String sessionId = jsonObject.get("id").asString(); 1000 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1001 return session.new Info(jsonObject); 1002 } 1003 1004 /** 1005 * Creates a new version of a file. 1006 * @param inputStream the stream instance that contains the data. 1007 * @param fileSize the size of the file that will be uploaded. 1008 * @return the created file instance. 1009 * @throws InterruptedException when a thread execution is interrupted. 1010 * @throws IOException when reading a stream throws exception. 1011 */ 1012 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize) 1013 throws InterruptedException, IOException { 1014 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1015 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize); 1016 } 1017 1018 /** 1019 * Creates a new version of a file using specified number of parallel http connections. 1020 * @param inputStream the stream instance that contains the data. 1021 * @param fileSize the size of the file that will be uploaded. 1022 * @param nParallelConnections number of parallel http connections to use 1023 * @param timeOut time to wait before killing the job 1024 * @param unit time unit for the time wait value 1025 * @return the created file instance. 1026 * @throws InterruptedException when a thread execution is interrupted. 1027 * @throws IOException when reading a stream throws exception. 1028 */ 1029 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1030 int nParallelConnections, long timeOut, TimeUnit unit) 1031 throws InterruptedException, IOException { 1032 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1033 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1034 .upload(this.getAPI(), inputStream, url, fileSize); 1035 } 1036 1037 /** 1038 * Contains information about a BoxFile. 1039 */ 1040 public class Info extends BoxItem.Info { 1041 private String sha1; 1042 private String versionNumber; 1043 private long commentCount; 1044 private EnumSet<Permission> permissions; 1045 private String extension; 1046 private boolean isPackage; 1047 private BoxFileVersion version; 1048 private URL previewLink; 1049 private BoxLock lock; 1050 private boolean isWatermarked; 1051 private JsonObject metadata; 1052 private Map<String, Map<String, Metadata>> metadataMap; 1053 private List<Representation> representations; 1054 1055 /** 1056 * Constructs an empty Info object. 1057 */ 1058 public Info() { 1059 super(); 1060 } 1061 1062 /** 1063 * Constructs an Info object by parsing information from a JSON string. 1064 * 1065 * @param json the JSON string to parse. 1066 */ 1067 public Info(String json) { 1068 super(json); 1069 } 1070 1071 /** 1072 * Constructs an Info object using an already parsed JSON object. 1073 * 1074 * @param jsonObject the parsed JSON object. 1075 */ 1076 public Info(JsonObject jsonObject) { 1077 super(jsonObject); 1078 } 1079 1080 @Override 1081 public BoxFile getResource() { 1082 return BoxFile.this; 1083 } 1084 1085 /** 1086 * Gets the SHA1 hash of the file. 1087 * 1088 * @return the SHA1 hash of the file. 1089 */ 1090 public String getSha1() { 1091 return this.sha1; 1092 } 1093 1094 /** 1095 * Gets the lock of the file. 1096 * 1097 * @return the lock of the file. 1098 */ 1099 public BoxLock getLock() { 1100 return this.lock; 1101 } 1102 1103 /** 1104 * Gets the current version number of the file. 1105 * 1106 * @return the current version number of the file. 1107 */ 1108 public String getVersionNumber() { 1109 return this.versionNumber; 1110 } 1111 1112 /** 1113 * Gets the number of comments on the file. 1114 * 1115 * @return the number of comments on the file. 1116 */ 1117 public long getCommentCount() { 1118 return this.commentCount; 1119 } 1120 1121 /** 1122 * Gets the permissions that the current user has on the file. 1123 * 1124 * @return the permissions that the current user has on the file. 1125 */ 1126 public EnumSet<Permission> getPermissions() { 1127 return this.permissions; 1128 } 1129 1130 /** 1131 * Gets the extension suffix of the file, excluding the dot. 1132 * 1133 * @return the extension of the file. 1134 */ 1135 public String getExtension() { 1136 return this.extension; 1137 } 1138 1139 /** 1140 * Gets whether or not the file is an OSX package. 1141 * 1142 * @return true if the file is an OSX package; otherwise false. 1143 */ 1144 public boolean getIsPackage() { 1145 return this.isPackage; 1146 } 1147 1148 /** 1149 * Gets the current version details of the file. 1150 * 1151 * @return the current version details of the file. 1152 */ 1153 public BoxFileVersion getVersion() { 1154 return this.version; 1155 } 1156 1157 /** 1158 * Gets the current expiring preview link. 1159 * 1160 * @return the expiring preview link 1161 */ 1162 public URL getPreviewLink() { 1163 return this.previewLink; 1164 } 1165 1166 /** 1167 * Gets flag indicating whether this file is Watermarked. 1168 * 1169 * @return whether the file is watermarked or not 1170 */ 1171 public boolean getIsWatermarked() { 1172 return this.isWatermarked; 1173 } 1174 1175 /** 1176 * Gets the metadata on this file associated with a specified scope and template. 1177 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found 1178 * then makes an API call to get metadata 1179 * @param templateName the metadata template type name. 1180 * @param scope the scope of the template (usually "global" or "enterprise"). 1181 * @return the metadata returned from the server. 1182 */ 1183 public Metadata getMetadata(String templateName, String scope) { 1184 try { 1185 return this.metadataMap.get(scope).get(templateName); 1186 } catch (NullPointerException e) { 1187 return null; 1188 } 1189 } 1190 1191 /** 1192 * Get file's representations. 1193 * @return list of representations 1194 */ 1195 public List<Representation> getRepresentations() { 1196 return this.representations; 1197 } 1198 1199 @Override 1200 protected void parseJSONMember(JsonObject.Member member) { 1201 super.parseJSONMember(member); 1202 1203 String memberName = member.getName(); 1204 JsonValue value = member.getValue(); 1205 if (memberName.equals("sha1")) { 1206 this.sha1 = value.asString(); 1207 } else if (memberName.equals("version_number")) { 1208 this.versionNumber = value.asString(); 1209 } else if (memberName.equals("comment_count")) { 1210 this.commentCount = value.asLong(); 1211 } else if (memberName.equals("permissions")) { 1212 this.permissions = this.parsePermissions(value.asObject()); 1213 } else if (memberName.equals("extension")) { 1214 this.extension = value.asString(); 1215 } else if (memberName.equals("is_package")) { 1216 this.isPackage = value.asBoolean(); 1217 } else if (memberName.equals("file_version")) { 1218 this.version = this.parseFileVersion(value.asObject()); 1219 } else if (memberName.equals("expiring_embed_link")) { 1220 try { 1221 String urlString = member.getValue().asObject().get("url").asString(); 1222 this.previewLink = new URL(urlString); 1223 } catch (MalformedURLException e) { 1224 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 1225 } 1226 } else if (memberName.equals("lock")) { 1227 if (value.isNull()) { 1228 this.lock = null; 1229 } else { 1230 this.lock = new BoxLock(value.asObject(), BoxFile.this.getAPI()); 1231 } 1232 } else if (memberName.equals("watermark_info")) { 1233 JsonObject jsonObject = value.asObject(); 1234 this.isWatermarked = jsonObject.get("is_watermarked").asBoolean(); 1235 } else if (memberName.equals("metadata")) { 1236 JsonObject jsonObject = value.asObject(); 1237 this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject); 1238 } else if (memberName.equals("representations")) { 1239 JsonObject jsonObject = value.asObject(); 1240 this.representations = Parsers.parseRepresentations(jsonObject); 1241 } 1242 } 1243 1244 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1245 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1246 for (JsonObject.Member member : jsonObject) { 1247 JsonValue value = member.getValue(); 1248 if (value.isNull() || !value.asBoolean()) { 1249 continue; 1250 } 1251 1252 String memberName = member.getName(); 1253 if (memberName.equals("can_download")) { 1254 permissions.add(Permission.CAN_DOWNLOAD); 1255 } else if (memberName.equals("can_upload")) { 1256 permissions.add(Permission.CAN_UPLOAD); 1257 } else if (memberName.equals("can_rename")) { 1258 permissions.add(Permission.CAN_RENAME); 1259 } else if (memberName.equals("can_delete")) { 1260 permissions.add(Permission.CAN_DELETE); 1261 } else if (memberName.equals("can_share")) { 1262 permissions.add(Permission.CAN_SHARE); 1263 } else if (memberName.equals("can_set_share_access")) { 1264 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 1265 } else if (memberName.equals("can_preview")) { 1266 permissions.add(Permission.CAN_PREVIEW); 1267 } else if (memberName.equals("can_comment")) { 1268 permissions.add(Permission.CAN_COMMENT); 1269 } 1270 } 1271 1272 return permissions; 1273 } 1274 1275 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 1276 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 1277 } 1278 } 1279 1280 /** 1281 * Enumerates the possible permissions that a user can have on a file. 1282 */ 1283 public enum Permission { 1284 /** 1285 * The user can download the file. 1286 */ 1287 CAN_DOWNLOAD("can_download"), 1288 1289 /** 1290 * The user can upload new versions of the file. 1291 */ 1292 CAN_UPLOAD("can_upload"), 1293 1294 /** 1295 * The user can rename the file. 1296 */ 1297 CAN_RENAME("can_rename"), 1298 1299 /** 1300 * The user can delete the file. 1301 */ 1302 CAN_DELETE("can_delete"), 1303 1304 /** 1305 * The user can share the file. 1306 */ 1307 CAN_SHARE("can_share"), 1308 1309 /** 1310 * The user can set the access level for shared links to the file. 1311 */ 1312 CAN_SET_SHARE_ACCESS("can_set_share_access"), 1313 1314 /** 1315 * The user can preview the file. 1316 */ 1317 CAN_PREVIEW("can_preview"), 1318 1319 /** 1320 * The user can comment on the file. 1321 */ 1322 CAN_COMMENT("can_comment"); 1323 1324 private final String jsonValue; 1325 1326 private Permission(String jsonValue) { 1327 this.jsonValue = jsonValue; 1328 } 1329 1330 static Permission fromJSONValue(String jsonValue) { 1331 return Permission.valueOf(jsonValue.toUpperCase()); 1332 } 1333 1334 String toJSONValue() { 1335 return this.jsonValue; 1336 } 1337 } 1338 1339 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 1340 Boolean notify, Boolean canViewPath) { 1341 BoxAPIConnection api = this.getAPI(); 1342 URL url = ADD_COLLABORATION_URL.build(api.getBaseURL()); 1343 1344 JsonObject itemField = new JsonObject(); 1345 itemField.add("id", this.getID()); 1346 itemField.add("type", "file"); 1347 1348 JsonObject requestJSON = new JsonObject(); 1349 requestJSON.add("item", itemField); 1350 requestJSON.add("accessible_by", accessibleByField); 1351 requestJSON.add("role", role.toJSONString()); 1352 if (canViewPath != null) { 1353 requestJSON.add("can_view_path", canViewPath.booleanValue()); 1354 } 1355 1356 BoxJSONRequest request = new BoxJSONRequest(api, url, "POST"); 1357 if (notify != null) { 1358 request.addHeader("notify", notify.toString()); 1359 } 1360 1361 request.setBody(requestJSON.toString()); 1362 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1363 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 1364 1365 BoxCollaboration newCollaboration = new BoxCollaboration(api, responseJSON.get("id").asString()); 1366 BoxCollaboration.Info info = newCollaboration.new Info(responseJSON); 1367 return info; 1368 } 1369 1370 /** 1371 * Adds a collaborator to this file. 1372 * 1373 * @param collaborator the collaborator to add. 1374 * @param role the role of the collaborator. 1375 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1376 * @param canViewPath whether view path collaboration feature is enabled or not. 1377 * @return info about the new collaboration. 1378 */ 1379 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1380 Boolean notify, Boolean canViewPath) { 1381 JsonObject accessibleByField = new JsonObject(); 1382 accessibleByField.add("id", collaborator.getID()); 1383 1384 if (collaborator instanceof BoxUser) { 1385 accessibleByField.add("type", "user"); 1386 } else if (collaborator instanceof BoxGroup) { 1387 accessibleByField.add("type", "group"); 1388 } else { 1389 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 1390 } 1391 return this.collaborate(accessibleByField, role, notify, canViewPath); 1392 } 1393 1394 1395 /** 1396 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1397 * account. 1398 * 1399 * @param email the email address of the collaborator to add. 1400 * @param role the role of the collaborator. 1401 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1402 * @param canViewPath whether view path collaboration feature is enabled or not. 1403 * @return info about the new collaboration. 1404 */ 1405 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1406 Boolean notify, Boolean canViewPath) { 1407 JsonObject accessibleByField = new JsonObject(); 1408 accessibleByField.add("login", email); 1409 accessibleByField.add("type", "user"); 1410 1411 return this.collaborate(accessibleByField, role, notify, canViewPath); 1412 } 1413 1414 /** 1415 * Used to retrieve all collaborations associated with the item. 1416 * 1417 * @param fields the optional fields to retrieve. 1418 * @return An iterable of metadata instances associated with the item. 1419 */ 1420 public BoxResourceIterable<BoxCollaboration.Info> getAllFileCollaborations(String... fields) { 1421 return BoxCollaboration.getAllFileCollaborations(this.getAPI(), this.getID(), 1422 GET_COLLABORATORS_PAGE_SIZE, fields); 1423 1424 } 1425}