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 * @return the lock returned from the server. 782 */ 783 public BoxLock lock() { 784 return this.lock(null, false); 785 } 786 787 /** 788 * Locks a file. 789 * 790 * @param isDownloadPrevented is downloading of file prevented when locked. 791 * @return the lock returned from the server. 792 */ 793 public BoxLock lock(boolean isDownloadPrevented) { 794 return this.lock(null, isDownloadPrevented); 795 } 796 797 /** 798 * Locks a file. 799 * 800 * @param expiresAt expiration date of the lock. 801 * @return the lock returned from the server. 802 */ 803 public BoxLock lock(Date expiresAt) { 804 return this.lock(expiresAt, false); 805 } 806 807 /** 808 * Locks a file. 809 * 810 * @param expiresAt expiration date of the lock. 811 * @param isDownloadPrevented is downloading of file prevented when locked. 812 * @return the lock returned from the server. 813 */ 814 public BoxLock lock(Date expiresAt, boolean isDownloadPrevented) { 815 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 816 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 817 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 818 819 JsonObject lockConfig = new JsonObject(); 820 lockConfig.add("type", "lock"); 821 if (expiresAt != null) { 822 lockConfig.add("expires_at", BoxDateFormat.format(expiresAt)); 823 } 824 lockConfig.add("is_download_prevented", isDownloadPrevented); 825 826 JsonObject requestJSON = new JsonObject(); 827 requestJSON.add("lock", lockConfig); 828 request.setBody(requestJSON.toString()); 829 830 BoxJSONResponse response = (BoxJSONResponse) request.send(); 831 832 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 833 JsonValue lockValue = responseJSON.get("lock"); 834 JsonObject lockJSON = JsonObject.readFrom(lockValue.toString()); 835 836 return new BoxLock(lockJSON, this.getAPI()); 837 } 838 839 /** 840 * Unlocks a file. 841 */ 842 public void unlock() { 843 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 844 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 845 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 846 847 JsonObject lockObject = new JsonObject(); 848 lockObject.add("lock", JsonObject.NULL); 849 850 request.setBody(lockObject.toString()); 851 request.send(); 852 } 853 854 /** 855 * Used to retrieve all metadata associated with the file. 856 * 857 * @param fields the optional fields to retrieve. 858 * @return An iterable of metadata instances associated with the file. 859 */ 860 public Iterable<Metadata> getAllMetadata(String... fields) { 861 return Metadata.getAllMetadata(this, fields); 862 } 863 864 /** 865 * Gets the file properties metadata. 866 * 867 * @return the metadata returned from the server. 868 */ 869 public Metadata getMetadata() { 870 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 871 } 872 873 /** 874 * Gets the file metadata of specified template type. 875 * 876 * @param typeName the metadata template type name. 877 * @return the metadata returned from the server. 878 */ 879 public Metadata getMetadata(String typeName) { 880 String scope = Metadata.scopeBasedOnType(typeName); 881 return this.getMetadata(typeName, scope); 882 } 883 884 /** 885 * Gets the file metadata of specified template type. 886 * 887 * @param typeName the metadata template type name. 888 * @param scope the metadata scope (global or enterprise). 889 * @return the metadata returned from the server. 890 */ 891 public Metadata getMetadata(String typeName, String scope) { 892 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 893 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 894 BoxJSONResponse response = (BoxJSONResponse) request.send(); 895 return new Metadata(JsonObject.readFrom(response.getJSON())); 896 } 897 898 /** 899 * Updates the file metadata. 900 * 901 * @param metadata the new metadata values. 902 * @return the metadata returned from the server. 903 */ 904 public Metadata updateMetadata(Metadata metadata) { 905 String scope; 906 if (metadata.getScope().equals(Metadata.GLOBAL_METADATA_SCOPE)) { 907 scope = Metadata.GLOBAL_METADATA_SCOPE; 908 } else { 909 scope = Metadata.ENTERPRISE_METADATA_SCOPE; 910 } 911 912 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), 913 scope, metadata.getTemplateName()); 914 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 915 request.addHeader("Content-Type", "application/json-patch+json"); 916 request.setBody(metadata.getPatch()); 917 BoxJSONResponse response = (BoxJSONResponse) request.send(); 918 return new Metadata(JsonObject.readFrom(response.getJSON())); 919 } 920 921 /** 922 * Deletes the file properties metadata. 923 */ 924 public void deleteMetadata() { 925 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 926 } 927 928 /** 929 * Deletes the file metadata of specified template type. 930 * 931 * @param typeName the metadata template type name. 932 */ 933 public void deleteMetadata(String typeName) { 934 String scope = Metadata.scopeBasedOnType(typeName); 935 this.deleteMetadata(typeName, scope); 936 } 937 938 /** 939 * Deletes the file metadata of specified template type. 940 * 941 * @param typeName the metadata template type name. 942 * @param scope the metadata scope (global or enterprise). 943 */ 944 public void deleteMetadata(String typeName, String scope) { 945 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 946 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 947 request.send(); 948 } 949 950 /** 951 * Used to retrieve the watermark for the file. 952 * If the file does not have a watermark applied to it, a 404 Not Found will be returned by API. 953 * 954 * @param fields the fields to retrieve. 955 * @return the watermark associated with the file. 956 */ 957 public BoxWatermark getWatermark(String... fields) { 958 return this.getWatermark(FILE_URL_TEMPLATE, fields); 959 } 960 961 /** 962 * Used to apply or update the watermark for the file. 963 * 964 * @return the watermark associated with the file. 965 */ 966 public BoxWatermark applyWatermark() { 967 return this.applyWatermark(FILE_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 968 } 969 970 /** 971 * Removes a watermark from the file. 972 * If the file did not have a watermark applied to it, a 404 Not Found will be returned by API. 973 */ 974 public void removeWatermark() { 975 this.removeWatermark(FILE_URL_TEMPLATE); 976 } 977 978 /** 979 * {@inheritDoc} 980 */ 981 @Override 982 public BoxFile.Info setCollections(BoxCollection... collections) { 983 JsonArray jsonArray = new JsonArray(); 984 for (BoxCollection collection : collections) { 985 JsonObject collectionJSON = new JsonObject(); 986 collectionJSON.add("id", collection.getID()); 987 jsonArray.add(collectionJSON); 988 } 989 JsonObject infoJSON = new JsonObject(); 990 infoJSON.add("collections", jsonArray); 991 992 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 993 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 994 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 995 request.setBody(infoJSON.toString()); 996 BoxJSONResponse response = (BoxJSONResponse) request.send(); 997 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 998 return new Info(jsonObject); 999 } 1000 1001 /** 1002 * Creates an upload session to create a new version of a file in chunks. 1003 * This will first verify that the version can be created and then open a session for uploading pieces of the file. 1004 * @param fileSize the size of the file that will be uploaded. 1005 * @return the created upload session instance. 1006 */ 1007 public BoxFileUploadSession.Info createUploadSession(long fileSize) { 1008 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1009 1010 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 1011 request.addHeader("Content-Type", "application/json"); 1012 1013 JsonObject body = new JsonObject(); 1014 body.add("file_size", fileSize); 1015 request.setBody(body.toString()); 1016 1017 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1018 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 1019 1020 String sessionId = jsonObject.get("id").asString(); 1021 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1022 return session.new Info(jsonObject); 1023 } 1024 1025 /** 1026 * Creates a new version of a file. 1027 * @param inputStream the stream instance that contains the data. 1028 * @param fileSize the size of the file that will be uploaded. 1029 * @return the created file instance. 1030 * @throws InterruptedException when a thread execution is interrupted. 1031 * @throws IOException when reading a stream throws exception. 1032 */ 1033 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize) 1034 throws InterruptedException, IOException { 1035 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1036 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize); 1037 } 1038 1039 /** 1040 * Creates a new version of a file using specified number of parallel http connections. 1041 * @param inputStream the stream instance that contains the data. 1042 * @param fileSize the size of the file that will be uploaded. 1043 * @param nParallelConnections number of parallel http connections to use 1044 * @param timeOut time to wait before killing the job 1045 * @param unit time unit for the time wait value 1046 * @return the created file instance. 1047 * @throws InterruptedException when a thread execution is interrupted. 1048 * @throws IOException when reading a stream throws exception. 1049 */ 1050 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1051 int nParallelConnections, long timeOut, TimeUnit unit) 1052 throws InterruptedException, IOException { 1053 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1054 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1055 .upload(this.getAPI(), inputStream, url, fileSize); 1056 } 1057 1058 /** 1059 * Contains information about a BoxFile. 1060 */ 1061 public class Info extends BoxItem.Info { 1062 private String sha1; 1063 private String versionNumber; 1064 private long commentCount; 1065 private EnumSet<Permission> permissions; 1066 private String extension; 1067 private boolean isPackage; 1068 private BoxFileVersion version; 1069 private URL previewLink; 1070 private BoxLock lock; 1071 private boolean isWatermarked; 1072 private JsonObject metadata; 1073 private Map<String, Map<String, Metadata>> metadataMap; 1074 private List<Representation> representations; 1075 1076 /** 1077 * Constructs an empty Info object. 1078 */ 1079 public Info() { 1080 super(); 1081 } 1082 1083 /** 1084 * Constructs an Info object by parsing information from a JSON string. 1085 * 1086 * @param json the JSON string to parse. 1087 */ 1088 public Info(String json) { 1089 super(json); 1090 } 1091 1092 /** 1093 * Constructs an Info object using an already parsed JSON object. 1094 * 1095 * @param jsonObject the parsed JSON object. 1096 */ 1097 public Info(JsonObject jsonObject) { 1098 super(jsonObject); 1099 } 1100 1101 @Override 1102 public BoxFile getResource() { 1103 return BoxFile.this; 1104 } 1105 1106 /** 1107 * Gets the SHA1 hash of the file. 1108 * 1109 * @return the SHA1 hash of the file. 1110 */ 1111 public String getSha1() { 1112 return this.sha1; 1113 } 1114 1115 /** 1116 * Gets the lock of the file. 1117 * 1118 * @return the lock of the file. 1119 */ 1120 public BoxLock getLock() { 1121 return this.lock; 1122 } 1123 1124 /** 1125 * Gets the current version number of the file. 1126 * 1127 * @return the current version number of the file. 1128 */ 1129 public String getVersionNumber() { 1130 return this.versionNumber; 1131 } 1132 1133 /** 1134 * Gets the number of comments on the file. 1135 * 1136 * @return the number of comments on the file. 1137 */ 1138 public long getCommentCount() { 1139 return this.commentCount; 1140 } 1141 1142 /** 1143 * Gets the permissions that the current user has on the file. 1144 * 1145 * @return the permissions that the current user has on the file. 1146 */ 1147 public EnumSet<Permission> getPermissions() { 1148 return this.permissions; 1149 } 1150 1151 /** 1152 * Gets the extension suffix of the file, excluding the dot. 1153 * 1154 * @return the extension of the file. 1155 */ 1156 public String getExtension() { 1157 return this.extension; 1158 } 1159 1160 /** 1161 * Gets whether or not the file is an OSX package. 1162 * 1163 * @return true if the file is an OSX package; otherwise false. 1164 */ 1165 public boolean getIsPackage() { 1166 return this.isPackage; 1167 } 1168 1169 /** 1170 * Gets the current version details of the file. 1171 * 1172 * @return the current version details of the file. 1173 */ 1174 public BoxFileVersion getVersion() { 1175 return this.version; 1176 } 1177 1178 /** 1179 * Gets the current expiring preview link. 1180 * 1181 * @return the expiring preview link 1182 */ 1183 public URL getPreviewLink() { 1184 return this.previewLink; 1185 } 1186 1187 /** 1188 * Gets flag indicating whether this file is Watermarked. 1189 * 1190 * @return whether the file is watermarked or not 1191 */ 1192 public boolean getIsWatermarked() { 1193 return this.isWatermarked; 1194 } 1195 1196 /** 1197 * Gets the metadata on this file associated with a specified scope and template. 1198 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found 1199 * then makes an API call to get metadata 1200 * @param templateName the metadata template type name. 1201 * @param scope the scope of the template (usually "global" or "enterprise"). 1202 * @return the metadata returned from the server. 1203 */ 1204 public Metadata getMetadata(String templateName, String scope) { 1205 try { 1206 return this.metadataMap.get(scope).get(templateName); 1207 } catch (NullPointerException e) { 1208 return null; 1209 } 1210 } 1211 1212 /** 1213 * Get file's representations. 1214 * @return list of representations 1215 */ 1216 public List<Representation> getRepresentations() { 1217 return this.representations; 1218 } 1219 1220 @Override 1221 protected void parseJSONMember(JsonObject.Member member) { 1222 super.parseJSONMember(member); 1223 1224 String memberName = member.getName(); 1225 JsonValue value = member.getValue(); 1226 if (memberName.equals("sha1")) { 1227 this.sha1 = value.asString(); 1228 } else if (memberName.equals("version_number")) { 1229 this.versionNumber = value.asString(); 1230 } else if (memberName.equals("comment_count")) { 1231 this.commentCount = value.asLong(); 1232 } else if (memberName.equals("permissions")) { 1233 this.permissions = this.parsePermissions(value.asObject()); 1234 } else if (memberName.equals("extension")) { 1235 this.extension = value.asString(); 1236 } else if (memberName.equals("is_package")) { 1237 this.isPackage = value.asBoolean(); 1238 } else if (memberName.equals("file_version")) { 1239 this.version = this.parseFileVersion(value.asObject()); 1240 } else if (memberName.equals("expiring_embed_link")) { 1241 try { 1242 String urlString = member.getValue().asObject().get("url").asString(); 1243 this.previewLink = new URL(urlString); 1244 } catch (MalformedURLException e) { 1245 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 1246 } 1247 } else if (memberName.equals("lock")) { 1248 if (value.isNull()) { 1249 this.lock = null; 1250 } else { 1251 this.lock = new BoxLock(value.asObject(), BoxFile.this.getAPI()); 1252 } 1253 } else if (memberName.equals("watermark_info")) { 1254 JsonObject jsonObject = value.asObject(); 1255 this.isWatermarked = jsonObject.get("is_watermarked").asBoolean(); 1256 } else if (memberName.equals("metadata")) { 1257 JsonObject jsonObject = value.asObject(); 1258 this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject); 1259 } else if (memberName.equals("representations")) { 1260 JsonObject jsonObject = value.asObject(); 1261 this.representations = Parsers.parseRepresentations(jsonObject); 1262 } 1263 } 1264 1265 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1266 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1267 for (JsonObject.Member member : jsonObject) { 1268 JsonValue value = member.getValue(); 1269 if (value.isNull() || !value.asBoolean()) { 1270 continue; 1271 } 1272 1273 String memberName = member.getName(); 1274 if (memberName.equals("can_download")) { 1275 permissions.add(Permission.CAN_DOWNLOAD); 1276 } else if (memberName.equals("can_upload")) { 1277 permissions.add(Permission.CAN_UPLOAD); 1278 } else if (memberName.equals("can_rename")) { 1279 permissions.add(Permission.CAN_RENAME); 1280 } else if (memberName.equals("can_delete")) { 1281 permissions.add(Permission.CAN_DELETE); 1282 } else if (memberName.equals("can_share")) { 1283 permissions.add(Permission.CAN_SHARE); 1284 } else if (memberName.equals("can_set_share_access")) { 1285 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 1286 } else if (memberName.equals("can_preview")) { 1287 permissions.add(Permission.CAN_PREVIEW); 1288 } else if (memberName.equals("can_comment")) { 1289 permissions.add(Permission.CAN_COMMENT); 1290 } 1291 } 1292 1293 return permissions; 1294 } 1295 1296 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 1297 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 1298 } 1299 } 1300 1301 /** 1302 * Enumerates the possible permissions that a user can have on a file. 1303 */ 1304 public enum Permission { 1305 /** 1306 * The user can download the file. 1307 */ 1308 CAN_DOWNLOAD("can_download"), 1309 1310 /** 1311 * The user can upload new versions of the file. 1312 */ 1313 CAN_UPLOAD("can_upload"), 1314 1315 /** 1316 * The user can rename the file. 1317 */ 1318 CAN_RENAME("can_rename"), 1319 1320 /** 1321 * The user can delete the file. 1322 */ 1323 CAN_DELETE("can_delete"), 1324 1325 /** 1326 * The user can share the file. 1327 */ 1328 CAN_SHARE("can_share"), 1329 1330 /** 1331 * The user can set the access level for shared links to the file. 1332 */ 1333 CAN_SET_SHARE_ACCESS("can_set_share_access"), 1334 1335 /** 1336 * The user can preview the file. 1337 */ 1338 CAN_PREVIEW("can_preview"), 1339 1340 /** 1341 * The user can comment on the file. 1342 */ 1343 CAN_COMMENT("can_comment"); 1344 1345 private final String jsonValue; 1346 1347 private Permission(String jsonValue) { 1348 this.jsonValue = jsonValue; 1349 } 1350 1351 static Permission fromJSONValue(String jsonValue) { 1352 return Permission.valueOf(jsonValue.toUpperCase()); 1353 } 1354 1355 String toJSONValue() { 1356 return this.jsonValue; 1357 } 1358 } 1359 1360 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 1361 Boolean notify, Boolean canViewPath) { 1362 BoxAPIConnection api = this.getAPI(); 1363 URL url = ADD_COLLABORATION_URL.build(api.getBaseURL()); 1364 1365 JsonObject itemField = new JsonObject(); 1366 itemField.add("id", this.getID()); 1367 itemField.add("type", "file"); 1368 1369 JsonObject requestJSON = new JsonObject(); 1370 requestJSON.add("item", itemField); 1371 requestJSON.add("accessible_by", accessibleByField); 1372 requestJSON.add("role", role.toJSONString()); 1373 if (canViewPath != null) { 1374 requestJSON.add("can_view_path", canViewPath.booleanValue()); 1375 } 1376 1377 BoxJSONRequest request = new BoxJSONRequest(api, url, "POST"); 1378 if (notify != null) { 1379 request.addHeader("notify", notify.toString()); 1380 } 1381 1382 request.setBody(requestJSON.toString()); 1383 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1384 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 1385 1386 BoxCollaboration newCollaboration = new BoxCollaboration(api, responseJSON.get("id").asString()); 1387 BoxCollaboration.Info info = newCollaboration.new Info(responseJSON); 1388 return info; 1389 } 1390 1391 /** 1392 * Adds a collaborator to this file. 1393 * 1394 * @param collaborator the collaborator to add. 1395 * @param role the role of the collaborator. 1396 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1397 * @param canViewPath whether view path collaboration feature is enabled or not. 1398 * @return info about the new collaboration. 1399 */ 1400 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1401 Boolean notify, Boolean canViewPath) { 1402 JsonObject accessibleByField = new JsonObject(); 1403 accessibleByField.add("id", collaborator.getID()); 1404 1405 if (collaborator instanceof BoxUser) { 1406 accessibleByField.add("type", "user"); 1407 } else if (collaborator instanceof BoxGroup) { 1408 accessibleByField.add("type", "group"); 1409 } else { 1410 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 1411 } 1412 return this.collaborate(accessibleByField, role, notify, canViewPath); 1413 } 1414 1415 1416 /** 1417 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1418 * account. 1419 * 1420 * @param email the email address of the collaborator to add. 1421 * @param role the role of the collaborator. 1422 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1423 * @param canViewPath whether view path collaboration feature is enabled or not. 1424 * @return info about the new collaboration. 1425 */ 1426 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1427 Boolean notify, Boolean canViewPath) { 1428 JsonObject accessibleByField = new JsonObject(); 1429 accessibleByField.add("login", email); 1430 accessibleByField.add("type", "user"); 1431 1432 return this.collaborate(accessibleByField, role, notify, canViewPath); 1433 } 1434 1435 /** 1436 * Used to retrieve all collaborations associated with the item. 1437 * 1438 * @param fields the optional fields to retrieve. 1439 * @return An iterable of metadata instances associated with the item. 1440 */ 1441 public BoxResourceIterable<BoxCollaboration.Info> getAllFileCollaborations(String... fields) { 1442 return BoxCollaboration.getAllFileCollaborations(this.getAPI(), this.getID(), 1443 GET_COLLABORATORS_PAGE_SIZE, fields); 1444 1445 } 1446}