001package com.box.sdk; 002 003import static com.box.sdk.PagingParameters.DEFAULT_LIMIT; 004import static com.box.sdk.PagingParameters.marker; 005import static com.box.sdk.PagingParameters.offset; 006import static com.box.sdk.http.ContentType.APPLICATION_JSON_PATCH; 007 008import com.box.sdk.internal.utils.Parsers; 009import com.box.sdk.sharedlink.BoxSharedLinkRequest; 010import com.eclipsesource.json.Json; 011import com.eclipsesource.json.JsonArray; 012import com.eclipsesource.json.JsonObject; 013import com.eclipsesource.json.JsonValue; 014import java.io.IOException; 015import java.io.InputStream; 016import java.net.URL; 017import java.util.ArrayList; 018import java.util.Collection; 019import java.util.Date; 020import java.util.EnumSet; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025import java.util.concurrent.TimeUnit; 026 027/** 028 * <p>Represents a folder on Box. This class can be used to iterate through a folder's contents, collaborate a folder with 029 * another user or group, and perform other common folder operations (move, copy, delete, etc.). 030 * </p> 031 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 032 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 033 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p> 034 */ 035@BoxResourceType("folder") 036public class BoxFolder extends BoxItem implements Iterable<BoxItem.Info> { 037 /** 038 * An array of all possible folder fields that can be requested when calling {@link #getInfo(String...)}. 039 */ 040 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "name", "created_at", "modified_at", 041 "description", "size", "path_collection", "created_by", "modified_by", "trashed_at", "purged_at", 042 "content_created_at", "content_modified_at", "owned_by", "shared_link", "folder_upload_email", "parent", 043 "item_status", "item_collection", "sync_state", "has_collaborations", "permissions", "tags", 044 "can_non_owners_invite", "collections", "watermark_info", "metadata", "is_externally_owned", 045 "is_collaboration_restricted_to_enterprise", "allowed_shared_link_access_levels", "allowed_invitee_roles", 046 "is_accessible_via_shared_link" 047 }; 048 /** 049 * Create Folder URL Template. 050 */ 051 public static final URLTemplate CREATE_FOLDER_URL = new URLTemplate("folders"); 052 /** 053 * Create Web Link URL Template. 054 */ 055 public static final URLTemplate CREATE_WEB_LINK_URL = new URLTemplate("web_links"); 056 /** 057 * Copy Folder URL Template. 058 */ 059 public static final URLTemplate COPY_FOLDER_URL = new URLTemplate("folders/%s/copy"); 060 /** 061 * Delete Folder URL Template. 062 */ 063 public static final URLTemplate DELETE_FOLDER_URL = new URLTemplate("folders/%s?recursive=%b"); 064 /** 065 * Folder Info URL Template. 066 */ 067 public static final URLTemplate FOLDER_INFO_URL_TEMPLATE = new URLTemplate("folders/%s"); 068 /** 069 * Upload File URL Template. 070 */ 071 public static final URLTemplate UPLOAD_FILE_URL = new URLTemplate("files/content"); 072 /** 073 * Add Collaboration URL Template. 074 */ 075 public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations"); 076 /** 077 * Get Collaborations URL Template. 078 */ 079 public static final URLTemplate GET_COLLABORATIONS_URL = new URLTemplate("folders/%s/collaborations"); 080 /** 081 * Get Items URL Template. 082 */ 083 public static final URLTemplate GET_ITEMS_URL = new URLTemplate("folders/%s/items/"); 084 /** 085 * Search URL Template. 086 */ 087 public static final URLTemplate SEARCH_URL_TEMPLATE = new URLTemplate("search"); 088 /** 089 * Metadata URL Template. 090 */ 091 public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("folders/%s/metadata/%s/%s"); 092 /** 093 * Upload Session URL Template. 094 */ 095 public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions"); 096 /** 097 * Folder Locks URL Template. 098 */ 099 public static final URLTemplate FOLDER_LOCK_URL_TEMPLATE = new URLTemplate("folder_locks"); 100 /** 101 * Describes folder item type. 102 */ 103 static final String TYPE = "folder"; 104 105 /** 106 * Constructs a BoxFolder for a folder with a given ID. 107 * 108 * @param api the API connection to be used by the folder. 109 * @param id the ID of the folder. 110 */ 111 public BoxFolder(BoxAPIConnection api, String id) { 112 super(api, id); 113 } 114 115 /** 116 * Gets the current user's root folder. 117 * 118 * @param api the API connection to be used by the folder. 119 * @return the user's root folder. 120 */ 121 public static BoxFolder getRootFolder(BoxAPIConnection api) { 122 return new BoxFolder(api, "0"); 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 @Override 129 protected URL getItemURL() { 130 return FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 131 } 132 133 /** 134 * Adds a collaborator to this folder. 135 * 136 * @param collaborator the collaborator to add. 137 * @param role the role of the collaborator. 138 * @return info about the new collaboration. 139 */ 140 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role) { 141 JsonObject accessibleByField = new JsonObject(); 142 accessibleByField.add("id", collaborator.getID()); 143 144 if (collaborator instanceof BoxUser) { 145 accessibleByField.add("type", "user"); 146 } else if (collaborator instanceof BoxGroup) { 147 accessibleByField.add("type", "group"); 148 } else { 149 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 150 } 151 152 return this.collaborate(accessibleByField, role, null, null, null, null); 153 } 154 155 /** 156 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 157 * account. 158 * 159 * @param email the email address of the collaborator to add. 160 * @param role the role of the collaborator. 161 * @return info about the new collaboration. 162 */ 163 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role) { 164 JsonObject accessibleByField = new JsonObject(); 165 accessibleByField.add("login", email); 166 accessibleByField.add("type", "user"); 167 168 return this.collaborate(accessibleByField, role, null, null, null, null); 169 } 170 171 /** 172 * Adds a collaborator to this folder. 173 * 174 * @param collaborator the collaborator to add. 175 * @param role the role of the collaborator. 176 * @param notify the user/group should receive email notification of the collaboration or not. 177 * @param canViewPath the view path collaboration feature is enabled or not. 178 * View path collaborations allow the invitee to see the entire ancestral path to the associated 179 * folder. The user will not gain privileges in any ancestral folder. 180 * @param expiresAt when the collaboration should expire. 181 * @param isAccessOnly whether the collaboration is access only or not. 182 * @return info about the new collaboration. 183 */ 184 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 185 Boolean notify, Boolean canViewPath, 186 Date expiresAt, Boolean isAccessOnly) { 187 JsonObject accessibleByField = new JsonObject(); 188 accessibleByField.add("id", collaborator.getID()); 189 190 if (collaborator instanceof BoxUser) { 191 accessibleByField.add("type", "user"); 192 } else if (collaborator instanceof BoxGroup) { 193 accessibleByField.add("type", "group"); 194 } else { 195 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 196 } 197 198 return this.collaborate(accessibleByField, role, notify, canViewPath, expiresAt, isAccessOnly); 199 } 200 201 /** 202 * Adds a collaborator to this folder. 203 * 204 * @param collaborator the collaborator to add. 205 * @param role the role of the collaborator. 206 * @param notify the user/group should receive email notification of the collaboration or not. 207 * @param canViewPath the view path collaboration feature is enabled or not. 208 * View path collaborations allow the invitee to see the entire ancestral path to the associated 209 * folder. The user will not gain privileges in any ancestral folder. 210 * @return info about the new collaboration. 211 */ 212 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 213 Boolean notify, Boolean canViewPath) { 214 return this.collaborate(collaborator, role, notify, canViewPath, null, null); 215 } 216 217 /** 218 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 219 * account. 220 * 221 * @param email the email address of the collaborator to add. 222 * @param role the role of the collaborator. 223 * @param notify the user/group should receive email notification of the collaboration or not. 224 * @param canViewPath the view path collaboration feature is enabled or not. 225 * View path collaborations allow the invitee to see the entire ancestral path to the associated 226 * folder. The user will not gain privileges in any ancestral folder. 227 * @param expiresAt when the collaboration should expire. 228 * @param isAccessOnly whether the collaboration is access only or not. 229 * @return info about the new collaboration. 230 */ 231 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 232 Boolean notify, Boolean canViewPath, 233 Date expiresAt, Boolean isAccessOnly) { 234 JsonObject accessibleByField = new JsonObject(); 235 accessibleByField.add("login", email); 236 accessibleByField.add("type", "user"); 237 238 return this.collaborate(accessibleByField, role, notify, canViewPath, expiresAt, isAccessOnly); 239 } 240 241 /** 242 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 243 * account. 244 * 245 * @param email the email address of the collaborator to add. 246 * @param role the role of the collaborator. 247 * @param notify the user/group should receive email notification of the collaboration or not. 248 * @param canViewPath the view path collaboration feature is enabled or not. 249 * View path collaborations allow the invitee to see the entire ancestral path to the associated 250 * folder. The user will not gain privileges in any ancestral folder. 251 * @return info about the new collaboration. 252 */ 253 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 254 Boolean notify, Boolean canViewPath) { 255 return this.collaborate(email, role, notify, canViewPath, null, null); 256 } 257 258 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 259 Boolean notify, Boolean canViewPath, 260 Date expiresAt, Boolean isAccessOnly) { 261 262 JsonObject itemField = new JsonObject(); 263 itemField.add("id", this.getID()); 264 itemField.add("type", "folder"); 265 266 return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath, 267 expiresAt, isAccessOnly); 268 } 269 270 /** 271 * Creates a shared link. 272 * 273 * @param sharedLinkRequest Shared link to create 274 * @return Created shared link. 275 */ 276 public BoxSharedLink createSharedLink(BoxSharedLinkRequest sharedLinkRequest) { 277 return createSharedLink(sharedLinkRequest.asSharedLink()); 278 } 279 280 private BoxSharedLink createSharedLink(BoxSharedLink sharedLink) { 281 BoxFolder.Info info = new BoxFolder.Info(); 282 info.setSharedLink(removeCanEditPermissionIfSet(sharedLink)); 283 284 this.updateInfo(info); 285 return info.getSharedLink(); 286 } 287 288 private BoxSharedLink removeCanEditPermissionIfSet(BoxSharedLink sharedLink) { 289 if (sharedLink.getPermissions() != null && sharedLink.getPermissions().getCanEdit()) { 290 BoxSharedLink.Permissions permissions = sharedLink.getPermissions(); 291 sharedLink.setPermissions( 292 new BoxSharedLink.Permissions(permissions.getCanPreview(), permissions.getCanDownload(), false) 293 ); 294 } 295 return sharedLink; 296 } 297 298 /** 299 * Gets information about all of the collaborations for this folder. 300 * 301 * @return a collection of information about the collaborations for this folder. 302 */ 303 public Collection<BoxCollaboration.Info> getCollaborations(String... fields) { 304 BoxAPIConnection api = this.getAPI(); 305 QueryStringBuilder queryBuilder = new QueryStringBuilder(); 306 if (fields.length > 0) { 307 queryBuilder.appendParam("fields", fields); 308 } 309 URL url = GET_COLLABORATIONS_URL.buildWithQuery(api.getBaseURL(), queryBuilder.toString(), this.getID()); 310 311 312 BoxJSONRequest request = new BoxJSONRequest(api, url, "GET"); 313 try (BoxJSONResponse response = request.send()) { 314 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 315 316 int entriesCount = responseJSON.get("total_count").asInt(); 317 Collection<BoxCollaboration.Info> collaborations = new ArrayList<>(entriesCount); 318 JsonArray entries = responseJSON.get("entries").asArray(); 319 for (JsonValue entry : entries) { 320 JsonObject entryObject = entry.asObject(); 321 BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString()); 322 BoxCollaboration.Info info = collaboration.new Info(entryObject); 323 collaborations.add(info); 324 } 325 326 return collaborations; 327 } 328 } 329 330 @Override 331 public BoxFolder.Info getInfo(String... fields) { 332 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 333 if (fields.length > 0) { 334 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 335 url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 336 } 337 338 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 339 try (BoxJSONResponse response = request.send()) { 340 return new Info(response.getJSON()); 341 } 342 } 343 344 /** 345 * Updates the information about this folder with any info fields that have been modified locally. 346 * 347 * @param info the updated info. 348 */ 349 public void updateInfo(BoxFolder.Info info) { 350 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 351 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 352 request.setBody(info.getPendingChanges()); 353 try (BoxJSONResponse response = request.send()) { 354 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 355 info.update(jsonObject); 356 } 357 } 358 359 @Override 360 public BoxFolder.Info copy(BoxFolder destination) { 361 return this.copy(destination, null); 362 } 363 364 @Override 365 public BoxFolder.Info copy(BoxFolder destination, String newName) { 366 URL url = COPY_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID()); 367 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 368 369 JsonObject parent = new JsonObject(); 370 parent.add("id", destination.getID()); 371 372 JsonObject copyInfo = new JsonObject(); 373 copyInfo.add("parent", parent); 374 if (newName != null) { 375 copyInfo.add("name", newName); 376 } 377 378 request.setBody(copyInfo.toString()); 379 try (BoxJSONResponse response = request.send()) { 380 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 381 BoxFolder copiedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 382 return copiedFolder.new Info(responseJSON); 383 } 384 } 385 386 /** 387 * Creates a new child folder inside this folder. 388 * 389 * @param name the new folder's name. 390 * @return the created folder's info. 391 */ 392 public BoxFolder.Info createFolder(String name) { 393 JsonObject parent = new JsonObject(); 394 parent.add("id", this.getID()); 395 396 JsonObject newFolder = new JsonObject(); 397 newFolder.add("name", name); 398 newFolder.add("parent", parent); 399 400 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), CREATE_FOLDER_URL.build(this.getAPI().getBaseURL()), 401 "POST"); 402 request.setBody(newFolder.toString()); 403 try (BoxJSONResponse response = request.send()) { 404 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 405 406 BoxFolder createdFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 407 return createdFolder.new Info(responseJSON); 408 } 409 } 410 411 /** 412 * Deletes this folder, optionally recursively deleting all of its contents. 413 * 414 * @param recursive true to recursively delete this folder's contents; otherwise false. 415 */ 416 public void delete(boolean recursive) { 417 URL url = DELETE_FOLDER_URL.buildAlpha(this.getAPI().getBaseURL(), this.getID(), recursive); 418 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 419 request.send().close(); 420 } 421 422 @Override 423 public BoxItem.Info move(BoxFolder destination) { 424 return this.move(destination, null); 425 } 426 427 @Override 428 public BoxItem.Info move(BoxFolder destination, String newName) { 429 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 430 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 431 432 JsonObject parent = new JsonObject(); 433 parent.add("id", destination.getID()); 434 435 JsonObject updateInfo = new JsonObject(); 436 updateInfo.add("parent", parent); 437 if (newName != null) { 438 updateInfo.add("name", newName); 439 } 440 441 request.setBody(updateInfo.toString()); 442 try (BoxJSONResponse response = request.send()) { 443 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 444 BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 445 return movedFolder.new Info(responseJSON); 446 } 447 } 448 449 /** 450 * Renames this folder. 451 * 452 * @param newName the new name of the folder. 453 */ 454 public void rename(String newName) { 455 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 456 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 457 458 JsonObject updateInfo = new JsonObject(); 459 updateInfo.add("name", newName); 460 461 request.setBody(updateInfo.toString()); 462 try (BoxJSONResponse response = request.send()) { 463 response.getJSON(); 464 } 465 } 466 467 /** 468 * Checks if the file can be successfully uploaded by using the preflight check. 469 * 470 * @param name the name to give the uploaded file. 471 * @param fileSize the size of the file used for account capacity calculations. 472 */ 473 public void canUpload(String name, long fileSize) { 474 URL url = UPLOAD_FILE_URL.build(this.getAPI().getBaseURL()); 475 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 476 477 JsonObject parent = new JsonObject(); 478 parent.add("id", this.getID()); 479 480 JsonObject preflightInfo = new JsonObject(); 481 preflightInfo.add("parent", parent); 482 preflightInfo.add("name", name); 483 484 preflightInfo.add("size", fileSize); 485 486 request.setBody(preflightInfo.toString()); 487 try (BoxJSONResponse response = request.send()) { 488 response.getJSON(); 489 } 490 } 491 492 /** 493 * Uploads a new file to this folder. 494 * 495 * @param fileContent a stream containing the contents of the file to upload. 496 * @param name the name to give the uploaded file. 497 * @return the uploaded file's info. 498 */ 499 public BoxFile.Info uploadFile(InputStream fileContent, String name) { 500 FileUploadParams uploadInfo = new FileUploadParams() 501 .setContent(fileContent) 502 .setName(name); 503 return this.uploadFile(uploadInfo); 504 } 505 506 /** 507 * Uploads a new file to this folder. 508 * 509 * @param callback the callback which allows file content to be written on output stream. 510 * @param name the name to give the uploaded file. 511 * @return the uploaded file's info. 512 */ 513 public BoxFile.Info uploadFile(UploadFileCallback callback, String name) { 514 FileUploadParams uploadInfo = new FileUploadParams() 515 .setUploadFileCallback(callback) 516 .setName(name); 517 return this.uploadFile(uploadInfo); 518 } 519 520 /** 521 * Uploads a new file to this folder while reporting the progress to a ProgressListener. 522 * 523 * @param fileContent a stream containing the contents of the file to upload. 524 * @param name the name to give the uploaded file. 525 * @param fileSize the size of the file used for determining the progress of the upload. 526 * @param listener a listener for monitoring the upload's progress. 527 * @return the uploaded file's info. 528 */ 529 public BoxFile.Info uploadFile(InputStream fileContent, String name, long fileSize, ProgressListener listener) { 530 FileUploadParams uploadInfo = new FileUploadParams() 531 .setContent(fileContent) 532 .setName(name) 533 .setSize(fileSize) 534 .setProgressListener(listener); 535 return this.uploadFile(uploadInfo); 536 } 537 538 /** 539 * Uploads a new file to this folder with a specified file description. 540 * 541 * @param fileContent a stream containing the contents of the file to upload. 542 * @param name the name to give the uploaded file. 543 * @param description the description to give the uploaded file. 544 * @return the uploaded file's info. 545 */ 546 public BoxFile.Info uploadFile(InputStream fileContent, String name, String description) { 547 FileUploadParams uploadInfo = new FileUploadParams() 548 .setContent(fileContent) 549 .setName(name) 550 .setDescription(description); 551 return this.uploadFile(uploadInfo); 552 } 553 554 /** 555 * Uploads a new file to this folder with custom upload parameters. 556 * 557 * @param uploadParams the custom upload parameters. 558 * @return the uploaded file's info. 559 */ 560 public BoxFile.Info uploadFile(FileUploadParams uploadParams) { 561 URL uploadURL = UPLOAD_FILE_URL.build(this.getAPI().getBaseUploadURL()); 562 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 563 564 JsonObject fieldJSON = new JsonObject(); 565 JsonObject parentIdJSON = new JsonObject(); 566 parentIdJSON.add("id", getID()); 567 fieldJSON.add("name", uploadParams.getName()); 568 fieldJSON.add("parent", parentIdJSON); 569 570 if (uploadParams.getCreated() != null) { 571 fieldJSON.add("content_created_at", BoxDateFormat.format(uploadParams.getCreated())); 572 } 573 574 if (uploadParams.getModified() != null) { 575 fieldJSON.add("content_modified_at", BoxDateFormat.format(uploadParams.getModified())); 576 } 577 578 if (uploadParams.getSHA1() != null && !uploadParams.getSHA1().isEmpty()) { 579 request.setContentSHA1(uploadParams.getSHA1()); 580 } 581 582 if (uploadParams.getDescription() != null) { 583 fieldJSON.add("description", uploadParams.getDescription()); 584 } 585 586 request.putField("attributes", fieldJSON.toString()); 587 588 if (uploadParams.getSize() > 0) { 589 request.setFile(uploadParams.getContent(), uploadParams.getName(), uploadParams.getSize()); 590 } else if (uploadParams.getContent() != null) { 591 request.setFile(uploadParams.getContent(), uploadParams.getName()); 592 } else { 593 request.setUploadFileCallback(uploadParams.getUploadFileCallback(), uploadParams.getName()); 594 } 595 596 BoxJSONResponse response = null; 597 try { 598 if (uploadParams.getProgressListener() == null) { 599 // upload files sends multipart request but response is JSON 600 response = (BoxJSONResponse) request.send(); 601 } else { 602 // upload files sends multipart request but response is JSON 603 response = (BoxJSONResponse) request.send(uploadParams.getProgressListener()); 604 } 605 JsonObject collection = Json.parse(response.getJSON()).asObject(); 606 JsonArray entries = collection.get("entries").asArray(); 607 JsonObject fileInfoJSON = entries.get(0).asObject(); 608 String uploadedFileID = fileInfoJSON.get("id").asString(); 609 610 BoxFile uploadedFile = new BoxFile(getAPI(), uploadedFileID); 611 return uploadedFile.new Info(fileInfoJSON); 612 } finally { 613 Optional.ofNullable(response).ifPresent(BoxAPIResponse::close); 614 } 615 } 616 617 /** 618 * Uploads a new weblink to this folder. 619 * 620 * @param linkURL the URL the weblink points to. 621 * @return the uploaded weblink's info. 622 */ 623 public BoxWebLink.Info createWebLink(URL linkURL) { 624 return this.createWebLink(null, linkURL, 625 null); 626 } 627 628 /** 629 * Uploads a new weblink to this folder. 630 * 631 * @param name the filename for the weblink. 632 * @param linkURL the URL the weblink points to. 633 * @return the uploaded weblink's info. 634 */ 635 public BoxWebLink.Info createWebLink(String name, URL linkURL) { 636 return this.createWebLink(name, linkURL, 637 null); 638 } 639 640 /** 641 * Uploads a new weblink to this folder. 642 * 643 * @param linkURL the URL the weblink points to. 644 * @param description the weblink's description. 645 * @return the uploaded weblink's info. 646 */ 647 public BoxWebLink.Info createWebLink(URL linkURL, String description) { 648 return this.createWebLink(null, linkURL, description); 649 } 650 651 /** 652 * Uploads a new weblink to this folder. 653 * 654 * @param name the filename for the weblink. 655 * @param linkURL the URL the weblink points to. 656 * @param description the weblink's description. 657 * @return the uploaded weblink's info. 658 */ 659 public BoxWebLink.Info createWebLink(String name, URL linkURL, String description) { 660 JsonObject parent = new JsonObject(); 661 parent.add("id", this.getID()); 662 663 JsonObject newWebLink = new JsonObject(); 664 newWebLink.add("name", name); 665 newWebLink.add("parent", parent); 666 newWebLink.add("url", linkURL.toString()); 667 668 if (description != null) { 669 newWebLink.add("description", description); 670 } 671 672 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), 673 CREATE_WEB_LINK_URL.build(this.getAPI().getBaseURL()), "POST"); 674 request.setBody(newWebLink.toString()); 675 try (BoxJSONResponse response = request.send()) { 676 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 677 678 BoxWebLink createdWebLink = new BoxWebLink(this.getAPI(), responseJSON.get("id").asString()); 679 return createdWebLink.new Info(responseJSON); 680 } 681 } 682 683 /** 684 * Returns an iterable containing the items in this folder. Iterating over the iterable returned by this method is 685 * equivalent to iterating over this BoxFolder directly. 686 * 687 * @return an iterable containing the items in this folder. 688 */ 689 public Iterable<BoxItem.Info> getChildren() { 690 return this; 691 } 692 693 /** 694 * Returns an iterable containing the items in this folder and specifies which child fields to retrieve from the 695 * API. 696 * 697 * @param fields the fields to retrieve. 698 * @return an iterable containing the items in this folder. 699 */ 700 public Iterable<BoxItem.Info> getChildren(final String... fields) { 701 return () -> { 702 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 703 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), queryString, getID()); 704 return new BoxItemIterator(getAPI(), url, marker(DEFAULT_LIMIT)); 705 }; 706 } 707 708 /** 709 * Returns an iterable containing the items in this folder sorted by name and direction. 710 * 711 * @param sort the field to sort by, can be set as `name`, `id`, and `date`. 712 * @param direction the direction to display the item results. 713 * @param fields the fields to retrieve. 714 * @return an iterable containing the items in this folder. 715 */ 716 public Iterable<BoxItem.Info> getChildren(String sort, SortDirection direction, final String... fields) { 717 QueryStringBuilder builder = new QueryStringBuilder() 718 .appendParam("sort", sort) 719 .appendParam("direction", direction.toString()); 720 721 if (fields.length > 0) { 722 builder.appendParam("fields", fields); 723 } 724 final String query = builder.toString(); 725 return () -> { 726 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), query, getID()); 727 return new BoxItemIterator(getAPI(), url, offset(0, DEFAULT_LIMIT)); 728 }; 729 } 730 731 /** 732 * Returns an iterable containing the items in this folder sorted by name and direction. 733 * 734 * @param sort the field to sort by, can be set as `name`, `id`, and `date`. 735 * @param direction the direction to display the item results. 736 * @param offset the index of the first child item to retrieve. 737 * @param limit the maximum number of children to retrieve after the offset. 738 * @param fields the fields to retrieve. 739 * @return an iterable containing the items in this folder. 740 */ 741 public Iterable<BoxItem.Info> getChildren(String sort, SortDirection direction, final long offset, final long limit, 742 final String... fields) { 743 QueryStringBuilder builder = new QueryStringBuilder() 744 .appendParam("sort", sort) 745 .appendParam("direction", direction.toString()); 746 747 if (fields.length > 0) { 748 builder.appendParam("fields", fields); 749 } 750 final String query = builder.toString(); 751 return () -> { 752 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), query, getID()); 753 return new BoxItemIterator(getAPI(), url, limit, offset); 754 }; 755 } 756 757 /** 758 * Retrieves a specific range of child items in this folder. 759 * 760 * @param offset the index of the first child item to retrieve. 761 * @param limit the maximum number of children to retrieve after the offset. 762 * @param fields the fields to retrieve. 763 * @return a partial collection containing the specified range of child items. 764 */ 765 public PartialCollection<BoxItem.Info> getChildrenRange(long offset, long limit, String... fields) { 766 QueryStringBuilder builder = new QueryStringBuilder() 767 .appendParam("limit", limit) 768 .appendParam("offset", offset); 769 770 if (fields.length > 0) { 771 builder.appendParam("fields", fields); 772 } 773 774 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), builder.toString(), getID()); 775 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 776 try (BoxJSONResponse response = request.send()) { 777 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 778 779 String totalCountString = responseJSON.get("total_count").toString(); 780 long fullSize = Double.valueOf(totalCountString).longValue(); 781 PartialCollection<BoxItem.Info> children = new PartialCollection<>(offset, limit, fullSize); 782 JsonArray jsonArray = responseJSON.get("entries").asArray(); 783 for (JsonValue value : jsonArray) { 784 JsonObject jsonObject = value.asObject(); 785 BoxItem.Info parsedItemInfo = (BoxItem.Info) BoxResource.parseInfo(this.getAPI(), jsonObject); 786 if (parsedItemInfo != null) { 787 children.add(parsedItemInfo); 788 } 789 } 790 return children; 791 } 792 } 793 794 /** 795 * Returns an iterable containing the items in this folder sorted by name and direction. 796 * 797 * @param sortParameters describes sorting parameters. 798 * Sort parameters are supported only with offset based pagination. 799 * Use {@link SortParameters#none()} to ignore sorting. 800 * @param pagingParameters describes paging parameters. 801 * @param fields the fields to retrieve. 802 * @return an iterable containing the items in this folder. 803 */ 804 public Iterable<BoxItem.Info> getChildren( 805 final SortParameters sortParameters, final PagingParameters pagingParameters, String... fields 806 ) { 807 QueryStringBuilder builder = sortParameters.asQueryStringBuilder(); 808 validateSortIsSelectedWithOffsetPaginationOnly(pagingParameters, builder); 809 810 if (fields.length > 0) { 811 builder.appendParam("fields", fields); 812 } 813 final String query = builder.toString(); 814 return () -> { 815 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), query, getID()); 816 return new BoxItemIterator(getAPI(), url, pagingParameters); 817 }; 818 } 819 820 /** 821 * Returns an iterator over the items in this folder. 822 * 823 * @return an iterator over the items in this folder. 824 */ 825 @Override 826 public Iterator<BoxItem.Info> iterator() { 827 URL url = GET_ITEMS_URL.build(this.getAPI().getBaseURL(), BoxFolder.this.getID()); 828 return new BoxItemIterator(BoxFolder.this.getAPI(), url, marker(DEFAULT_LIMIT)); 829 } 830 831 /** 832 * Adds new {@link BoxWebHook} to this {@link BoxFolder}. 833 * 834 * @param address {@link BoxWebHook.Info#getAddress()} 835 * @param triggers {@link BoxWebHook.Info#getTriggers()} 836 * @return created {@link BoxWebHook.Info} 837 */ 838 public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) { 839 return BoxWebHook.create(this, address, triggers); 840 } 841 842 /** 843 * Used to retrieve the watermark for the folder. 844 * If the folder does not have a watermark applied to it, a 404 Not Found will be returned by API. 845 * 846 * @param fields the fields to retrieve. 847 * @return the watermark associated with the folder. 848 */ 849 public BoxWatermark getWatermark(String... fields) { 850 return this.getWatermark(FOLDER_INFO_URL_TEMPLATE, fields); 851 } 852 853 /** 854 * Used to apply or update the watermark for the folder. 855 * 856 * @return the watermark associated with the folder. 857 */ 858 public BoxWatermark applyWatermark() { 859 return this.applyWatermark(FOLDER_INFO_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 860 } 861 862 /** 863 * Removes a watermark from the folder. 864 * If the folder did not have a watermark applied to it, a 404 Not Found will be returned by API. 865 */ 866 public void removeWatermark() { 867 this.removeWatermark(FOLDER_INFO_URL_TEMPLATE); 868 } 869 870 /** 871 * Used to retrieve all metadata associated with the folder. 872 * 873 * @param fields the optional fields to retrieve. 874 * @return An iterable of metadata instances associated with the folder 875 */ 876 public Iterable<Metadata> getAllMetadata(String... fields) { 877 return Metadata.getAllMetadata(this, fields); 878 } 879 880 @Override 881 public BoxFolder.Info setCollections(BoxCollection... collections) { 882 JsonArray jsonArray = new JsonArray(); 883 for (BoxCollection collection : collections) { 884 JsonObject collectionJSON = new JsonObject(); 885 collectionJSON.add("id", collection.getID()); 886 jsonArray.add(collectionJSON); 887 } 888 JsonObject infoJSON = new JsonObject(); 889 infoJSON.add("collections", jsonArray); 890 891 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 892 URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 893 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 894 request.setBody(infoJSON.toString()); 895 try (BoxJSONResponse response = request.send()) { 896 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 897 return new Info(jsonObject); 898 } 899 } 900 901 /** 902 * Creates global property metadata on this folder. 903 * 904 * @param metadata the new metadata values. 905 * @return the metadata returned from the server. 906 */ 907 public Metadata createMetadata(Metadata metadata) { 908 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 909 } 910 911 /** 912 * Creates metadata on this folder using a specified template. 913 * 914 * @param templateName the name of the metadata template. 915 * @param metadata the new metadata values. 916 * @return the metadata returned from the server. 917 */ 918 public Metadata createMetadata(String templateName, Metadata metadata) { 919 String scope = Metadata.scopeBasedOnType(templateName); 920 return this.createMetadata(templateName, scope, metadata); 921 } 922 923 /** 924 * Creates metadata on this folder using a specified scope and template. 925 * 926 * @param templateName the name of the metadata template. 927 * @param scope the scope of the template (usually "global" or "enterprise"). 928 * @param metadata the new metadata values. 929 * @return the metadata returned from the server. 930 */ 931 public Metadata createMetadata(String templateName, String scope, Metadata metadata) { 932 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 933 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 934 request.setBody(metadata.toString()); 935 try (BoxJSONResponse response = request.send()) { 936 return new Metadata(Json.parse(response.getJSON()).asObject()); 937 } 938 } 939 940 /** 941 * Sets the provided metadata on the folder. If metadata has already been created on this folder, 942 * it overwrites metadata keys specified in the `metadata` param. 943 * 944 * @param templateName the name of the metadata template. 945 * @param scope the scope of the template (usually "global" or "enterprise"). 946 * @param metadata the new metadata values. 947 * @return the metadata returned from the server. 948 */ 949 public Metadata setMetadata(String templateName, String scope, Metadata metadata) { 950 try { 951 return this.createMetadata(templateName, scope, metadata); 952 } catch (BoxAPIException e) { 953 if (e.getResponseCode() == 409) { 954 if (metadata.getOperations().isEmpty()) { 955 return getMetadata(); 956 } else { 957 return updateExistingTemplate(templateName, scope, metadata); 958 } 959 } else { 960 throw e; 961 } 962 } 963 } 964 965 /** 966 * Throws IllegalArgumentException exception when sorting and marker pagination is selected. 967 * 968 * @param pagingParameters paging definition to check 969 * @param sortQuery builder containing sort query 970 */ 971 private void validateSortIsSelectedWithOffsetPaginationOnly( 972 PagingParameters pagingParameters, 973 QueryStringBuilder sortQuery 974 ) { 975 if (pagingParameters != null && pagingParameters.isMarkerBasedPaging() && sortQuery.toString().length() > 0) { 976 throw new IllegalArgumentException("Sorting is not supported when using marker based pagination."); 977 } 978 } 979 980 private Metadata updateExistingTemplate(String templateName, String scope, Metadata metadata) { 981 Metadata metadataToUpdate = new Metadata(scope, templateName); 982 for (JsonValue value : metadata.getOperations()) { 983 if (value.asObject().get("value").isNumber()) { 984 metadataToUpdate.add(value.asObject().get("path").asString(), 985 value.asObject().get("value").asDouble()); 986 } else if (value.asObject().get("value").isString()) { 987 metadataToUpdate.add(value.asObject().get("path").asString(), 988 value.asObject().get("value").asString()); 989 } else if (value.asObject().get("value").isArray()) { 990 ArrayList<String> list = new ArrayList<>(); 991 for (JsonValue jsonValue : value.asObject().get("value").asArray()) { 992 list.add(jsonValue.asString()); 993 } 994 metadataToUpdate.add(value.asObject().get("path").asString(), list); 995 } 996 } 997 return this.updateMetadata(metadataToUpdate); 998 } 999 1000 /** 1001 * Gets the global properties metadata on this folder. 1002 * 1003 * @return the metadata returned from the server. 1004 */ 1005 public Metadata getMetadata() { 1006 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 1007 } 1008 1009 /** 1010 * Gets the metadata on this folder associated with a specified template. 1011 * 1012 * @param templateName the metadata template type name. 1013 * @return the metadata returned from the server. 1014 */ 1015 public Metadata getMetadata(String templateName) { 1016 String scope = Metadata.scopeBasedOnType(templateName); 1017 return this.getMetadata(templateName, scope); 1018 } 1019 1020 /** 1021 * Gets the metadata on this folder associated with a specified scope and template. 1022 * 1023 * @param templateName the metadata template type name. 1024 * @param scope the scope of the template (usually "global" or "enterprise"). 1025 * @return the metadata returned from the server. 1026 */ 1027 public Metadata getMetadata(String templateName, String scope) { 1028 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 1029 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET"); 1030 try (BoxJSONResponse response = request.send()) { 1031 return new Metadata(Json.parse(response.getJSON()).asObject()); 1032 } 1033 } 1034 1035 /** 1036 * Updates the folder metadata. 1037 * 1038 * @param metadata the new metadata values. 1039 * @return the metadata returned from the server. 1040 */ 1041 public Metadata updateMetadata(Metadata metadata) { 1042 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), metadata.getScope(), 1043 metadata.getTemplateName()); 1044 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT", APPLICATION_JSON_PATCH); 1045 request.setBody(metadata.getPatch()); 1046 try (BoxJSONResponse response = request.send()) { 1047 return new Metadata(Json.parse(response.getJSON()).asObject()); 1048 } 1049 } 1050 1051 /** 1052 * Deletes the global properties metadata on this folder. 1053 */ 1054 public void deleteMetadata() { 1055 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 1056 } 1057 1058 /** 1059 * Deletes the metadata on this folder associated with a specified template. 1060 * 1061 * @param templateName the metadata template type name. 1062 */ 1063 public void deleteMetadata(String templateName) { 1064 String scope = Metadata.scopeBasedOnType(templateName); 1065 this.deleteMetadata(templateName, scope); 1066 } 1067 1068 /** 1069 * Deletes the metadata on this folder associated with a specified scope and template. 1070 * 1071 * @param templateName the metadata template type name. 1072 * @param scope the scope of the template (usually "global" or "enterprise"). 1073 */ 1074 public void deleteMetadata(String templateName, String scope) { 1075 URL url = METADATA_URL_TEMPLATE.buildAlpha(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 1076 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 1077 request.send().close(); 1078 } 1079 1080 /** 1081 * Adds a metadata classification to the specified file. 1082 * 1083 * @param classificationType the metadata classification type. 1084 * @return the metadata classification type added to the file. 1085 */ 1086 public String addClassification(String classificationType) { 1087 Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType); 1088 Metadata classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, 1089 "enterprise", metadata); 1090 1091 return classification.getString(Metadata.CLASSIFICATION_KEY); 1092 } 1093 1094 /** 1095 * Updates a metadata classification on the specified file. 1096 * 1097 * @param classificationType the metadata classification type. 1098 * @return the new metadata classification type updated on the file. 1099 */ 1100 public String updateClassification(String classificationType) { 1101 Metadata metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY); 1102 metadata.replace(Metadata.CLASSIFICATION_KEY, classificationType); 1103 Metadata classification = this.updateMetadata(metadata); 1104 1105 return classification.getString(Metadata.CLASSIFICATION_KEY); 1106 } 1107 1108 /** 1109 * Attempts to add classification to a file. If classification already exists then do update. 1110 * 1111 * @param classificationType the metadata classification type. 1112 * @return the metadata classification type on the file. 1113 */ 1114 public String setClassification(String classificationType) { 1115 Metadata metadata = new Metadata().add(Metadata.CLASSIFICATION_KEY, classificationType); 1116 Metadata classification; 1117 1118 try { 1119 classification = this.createMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise", metadata); 1120 } catch (BoxAPIException e) { 1121 if (e.getResponseCode() == 409) { 1122 metadata = new Metadata("enterprise", Metadata.CLASSIFICATION_TEMPLATE_KEY); 1123 metadata.replace(Metadata.CLASSIFICATION_KEY, classificationType); 1124 classification = this.updateMetadata(metadata); 1125 } else { 1126 throw e; 1127 } 1128 } 1129 1130 return classification.getString("/Box__Security__Classification__Key"); 1131 } 1132 1133 /** 1134 * Gets the classification type for the specified file. 1135 * 1136 * @return the metadata classification type on the file. 1137 */ 1138 public String getClassification() { 1139 Metadata metadata = this.getMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY); 1140 return metadata.getString(Metadata.CLASSIFICATION_KEY); 1141 } 1142 1143 /** 1144 * Deletes the classification on the file. 1145 */ 1146 public void deleteClassification() { 1147 this.deleteMetadata(Metadata.CLASSIFICATION_TEMPLATE_KEY, "enterprise"); 1148 } 1149 1150 /** 1151 * Creates an upload session to create a new file in chunks. 1152 * This will first verify that the file can be created and then open a session for uploading pieces of the file. 1153 * 1154 * @param fileName the name of the file to be created 1155 * @param fileSize the size of the file that will be uploaded 1156 * @return the created upload session instance 1157 */ 1158 public BoxFileUploadSession.Info createUploadSession(String fileName, long fileSize) { 1159 1160 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 1161 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 1162 1163 JsonObject body = new JsonObject(); 1164 body.add("folder_id", this.getID()); 1165 body.add("file_name", fileName); 1166 body.add("file_size", fileSize); 1167 request.setBody(body.toString()); 1168 1169 try (BoxJSONResponse response = request.send()) { 1170 JsonObject jsonObject = Json.parse(response.getJSON()).asObject(); 1171 1172 String sessionId = jsonObject.get("id").asString(); 1173 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1174 1175 return session.new Info(jsonObject); 1176 } 1177 } 1178 1179 /** 1180 * Creates a new file. 1181 * 1182 * @param inputStream the stream instance that contains the data. 1183 * @param fileName the name of the file to be created. 1184 * @param fileSize the size of the file that will be uploaded. 1185 * @return the created file instance. 1186 * @throws InterruptedException when a thread execution is interrupted. 1187 * @throws IOException when reading a stream throws exception. 1188 */ 1189 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize) 1190 throws InterruptedException, IOException { 1191 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 1192 this.canUpload(fileName, fileSize); 1193 return new LargeFileUpload(). 1194 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize); 1195 } 1196 1197 /** 1198 * Creates a new file. Also sets file attributes. 1199 * 1200 * @param inputStream the stream instance that contains the data. 1201 * @param fileName the name of the file to be created. 1202 * @param fileSize the size of the file that will be uploaded. 1203 * @param fileAttributes file attributes to set 1204 * @return the created file instance. 1205 * @throws InterruptedException when a thread execution is interrupted. 1206 * @throws IOException when reading a stream throws exception. 1207 */ 1208 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize, 1209 Map<String, String> fileAttributes) 1210 throws InterruptedException, IOException { 1211 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 1212 this.canUpload(fileName, fileSize); 1213 return new LargeFileUpload(). 1214 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize, fileAttributes); 1215 } 1216 1217 /** 1218 * Creates a new file using specified number of parallel http connections. 1219 * 1220 * @param inputStream the stream instance that contains the data. 1221 * @param fileName the name of the file to be created. 1222 * @param fileSize the size of the file that will be uploaded. 1223 * @param nParallelConnections number of parallel http connections to use 1224 * @param timeOut time to wait before killing the job 1225 * @param unit time unit for the time wait value 1226 * @return the created file instance. 1227 * @throws InterruptedException when a thread execution is interrupted. 1228 * @throws IOException when reading a stream throws exception. 1229 */ 1230 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize, 1231 int nParallelConnections, long timeOut, TimeUnit unit) 1232 throws InterruptedException, IOException { 1233 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 1234 this.canUpload(fileName, fileSize); 1235 return new LargeFileUpload(nParallelConnections, timeOut, unit). 1236 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize); 1237 } 1238 1239 /** 1240 * Creates a new file using specified number of parallel http connections. Also sets file attributes. 1241 * 1242 * @param inputStream the stream instance that contains the data. 1243 * @param fileName the name of the file to be created. 1244 * @param fileSize the size of the file that will be uploaded. 1245 * @param nParallelConnections number of parallel http connections to use 1246 * @param timeOut time to wait before killing the job 1247 * @param unit time unit for the time wait value 1248 * @param fileAttributes file attributes to set 1249 * @return the created file instance. 1250 * @throws InterruptedException when a thread execution is interrupted. 1251 * @throws IOException when reading a stream throws exception. 1252 */ 1253 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize, 1254 int nParallelConnections, long timeOut, TimeUnit unit, 1255 Map<String, String> fileAttributes) 1256 throws InterruptedException, IOException { 1257 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 1258 this.canUpload(fileName, fileSize); 1259 return new LargeFileUpload(nParallelConnections, timeOut, unit). 1260 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize, fileAttributes); 1261 } 1262 1263 /** 1264 * Creates a new Metadata Cascade Policy on a folder. 1265 * 1266 * @param scope the scope of the metadata cascade policy. 1267 * @param templateKey the key of the template. 1268 * @return information about the Metadata Cascade Policy. 1269 */ 1270 public BoxMetadataCascadePolicy.Info addMetadataCascadePolicy(String scope, String templateKey) { 1271 1272 return BoxMetadataCascadePolicy.create(this.getAPI(), this.getID(), scope, templateKey); 1273 } 1274 1275 /** 1276 * Retrieves all Metadata Cascade Policies on a folder. 1277 * 1278 * @param fields optional fields to retrieve for cascade policies. 1279 * @return the Iterable of Box Metadata Cascade Policies in your enterprise. 1280 */ 1281 public Iterable<BoxMetadataCascadePolicy.Info> getMetadataCascadePolicies(String... fields) { 1282 return BoxMetadataCascadePolicy.getAll(this.getAPI(), this.getID(), fields); 1283 } 1284 1285 /** 1286 * Retrieves all Metadata Cascade Policies on a folder. 1287 * 1288 * @param enterpriseID the ID of the enterprise to retrieve cascade policies for. 1289 * @param limit the number of entries of cascade policies to retrieve. 1290 * @param fields optional fields to retrieve for cascade policies. 1291 * @return the Iterable of Box Metadata Cascade Policies in your enterprise. 1292 */ 1293 public Iterable<BoxMetadataCascadePolicy.Info> getMetadataCascadePolicies(String enterpriseID, 1294 int limit, String... fields) { 1295 1296 return BoxMetadataCascadePolicy.getAll(this.getAPI(), this.getID(), enterpriseID, limit, fields); 1297 } 1298 1299 /** 1300 * Lock this folder. 1301 * 1302 * @return a created folder lock object. 1303 */ 1304 public BoxFolderLock.Info lock() { 1305 JsonObject folderObject = new JsonObject(); 1306 folderObject.add("type", "folder"); 1307 folderObject.add("id", this.getID()); 1308 1309 JsonObject lockedOperations = new JsonObject(); 1310 lockedOperations.add("move", true); 1311 lockedOperations.add("delete", true); 1312 1313 1314 JsonObject body = new JsonObject(); 1315 body.add("folder", folderObject); 1316 body.add("locked_operations", lockedOperations); 1317 1318 BoxJSONRequest request = 1319 new BoxJSONRequest(this.getAPI(), FOLDER_LOCK_URL_TEMPLATE.build(this.getAPI().getBaseURL()), 1320 "POST"); 1321 request.setBody(body.toString()); 1322 try (BoxJSONResponse response = request.send()) { 1323 JsonObject responseJSON = Json.parse(response.getJSON()).asObject(); 1324 1325 BoxFolderLock createdFolderLock = new BoxFolderLock(this.getAPI(), responseJSON.get("id").asString()); 1326 return createdFolderLock.new Info(responseJSON); 1327 } 1328 } 1329 1330 /** 1331 * Get the lock on this folder. 1332 * 1333 * @return a folder lock object. 1334 */ 1335 public Iterable<BoxFolderLock.Info> getLocks() { 1336 String queryString = new QueryStringBuilder().appendParam("folder_id", this.getID()).toString(); 1337 final BoxAPIConnection api = this.getAPI(); 1338 return new BoxResourceIterable<BoxFolderLock.Info>(api, 1339 FOLDER_LOCK_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), queryString), 100) { 1340 @Override 1341 protected BoxFolderLock.Info factory(JsonObject jsonObject) { 1342 BoxFolderLock folderLock = 1343 new BoxFolderLock(api, jsonObject.get("id").asString()); 1344 return folderLock.new Info(jsonObject); 1345 } 1346 }; 1347 } 1348 1349 /** 1350 * Used to specify what direction to sort and display results. 1351 */ 1352 public enum SortDirection { 1353 /** 1354 * ASC for ascending order. 1355 */ 1356 ASC, 1357 1358 /** 1359 * DESC for descending order. 1360 */ 1361 DESC 1362 } 1363 1364 /** 1365 * Enumerates the possible sync states that a folder can have. 1366 */ 1367 public enum SyncState { 1368 /** 1369 * The folder is synced. 1370 */ 1371 SYNCED("synced"), 1372 1373 /** 1374 * The folder is not synced. 1375 */ 1376 NOT_SYNCED("not_synced"), 1377 1378 /** 1379 * The folder is partially synced. 1380 */ 1381 PARTIALLY_SYNCED("partially_synced"); 1382 1383 private final String jsonValue; 1384 1385 SyncState(String jsonValue) { 1386 this.jsonValue = jsonValue; 1387 } 1388 1389 static SyncState fromJSONValue(String jsonValue) { 1390 return SyncState.valueOf(jsonValue.toUpperCase()); 1391 } 1392 1393 String toJSONValue() { 1394 return this.jsonValue; 1395 } 1396 } 1397 1398 /** 1399 * Enumerates the possible permissions that a user can have on a folder. 1400 */ 1401 public enum Permission { 1402 /** 1403 * The user can download the folder. 1404 */ 1405 CAN_DOWNLOAD("can_download"), 1406 1407 /** 1408 * The user can upload to the folder. 1409 */ 1410 CAN_UPLOAD("can_upload"), 1411 1412 /** 1413 * The user can rename the folder. 1414 */ 1415 CAN_RENAME("can_rename"), 1416 1417 /** 1418 * The user can delete the folder. 1419 */ 1420 CAN_DELETE("can_delete"), 1421 1422 /** 1423 * The user can share the folder. 1424 */ 1425 CAN_SHARE("can_share"), 1426 1427 /** 1428 * The user can invite collaborators to the folder. 1429 */ 1430 CAN_INVITE_COLLABORATOR("can_invite_collaborator"), 1431 1432 /** 1433 * The user can set the access level for shared links to the folder. 1434 */ 1435 CAN_SET_SHARE_ACCESS("can_set_share_access"); 1436 1437 private final String jsonValue; 1438 1439 Permission(String jsonValue) { 1440 this.jsonValue = jsonValue; 1441 } 1442 1443 static Permission fromJSONValue(String jsonValue) { 1444 return Permission.valueOf(jsonValue.toUpperCase()); 1445 } 1446 1447 String toJSONValue() { 1448 return this.jsonValue; 1449 } 1450 } 1451 1452 /** 1453 * Contains information about a BoxFolder. 1454 */ 1455 public class Info extends BoxItem.Info { 1456 private BoxUploadEmail uploadEmail; 1457 private boolean hasCollaborations; 1458 private SyncState syncState; 1459 private EnumSet<Permission> permissions; 1460 private boolean canNonOwnersInvite; 1461 private boolean isWatermarked; 1462 private boolean isCollaborationRestrictedToEnterprise; 1463 private boolean isExternallyOwned; 1464 private Map<String, Map<String, Metadata>> metadataMap; 1465 private List<String> allowedSharedLinkAccessLevels; 1466 private List<String> allowedInviteeRoles; 1467 private BoxClassification classification; 1468 1469 private boolean isAccessibleViaSharedLink; 1470 1471 /** 1472 * Constructs an empty Info object. 1473 */ 1474 public Info() { 1475 super(); 1476 } 1477 1478 /** 1479 * Constructs an Info object by parsing information from a JSON string. 1480 * 1481 * @param json the JSON string to parse. 1482 */ 1483 public Info(String json) { 1484 super(json); 1485 } 1486 1487 /** 1488 * Constructs an Info object using an already parsed JSON object. 1489 * 1490 * @param jsonObject the parsed JSON object. 1491 */ 1492 public Info(JsonObject jsonObject) { 1493 super(jsonObject); 1494 } 1495 1496 /** 1497 * Gets the upload email for the folder. 1498 * 1499 * @return the upload email for the folder. 1500 */ 1501 public BoxUploadEmail getUploadEmail() { 1502 return this.uploadEmail; 1503 } 1504 1505 /** 1506 * Sets the upload email for the folder. 1507 * 1508 * @param uploadEmail the upload email for the folder. 1509 */ 1510 public void setUploadEmail(BoxUploadEmail uploadEmail) { 1511 if (this.uploadEmail == uploadEmail) { 1512 return; 1513 } 1514 1515 this.removeChildObject("folder_upload_email"); 1516 this.uploadEmail = uploadEmail; 1517 1518 if (uploadEmail == null) { 1519 this.addPendingChange("folder_upload_email", (String) null); 1520 } else { 1521 this.addChildObject("folder_upload_email", uploadEmail); 1522 } 1523 } 1524 1525 /** 1526 * Gets whether or not the folder has any collaborations. 1527 * 1528 * @return true if the folder has collaborations; otherwise false. 1529 */ 1530 public boolean getHasCollaborations() { 1531 return this.hasCollaborations; 1532 } 1533 1534 /** 1535 * Gets the sync state of the folder. 1536 * 1537 * @return the sync state of the folder. 1538 */ 1539 public SyncState getSyncState() { 1540 return this.syncState; 1541 } 1542 1543 /** 1544 * Sets the sync state of the folder. 1545 * 1546 * @param syncState the sync state of the folder. 1547 */ 1548 public void setSyncState(SyncState syncState) { 1549 this.syncState = syncState; 1550 this.addPendingChange("sync_state", syncState.toJSONValue()); 1551 } 1552 1553 /** 1554 * Gets the permissions that the current user has on the folder. 1555 * 1556 * @return the permissions that the current user has on the folder. 1557 */ 1558 public EnumSet<Permission> getPermissions() { 1559 return this.permissions; 1560 } 1561 1562 /** 1563 * Gets whether or not the non-owners can invite collaborators to the folder. 1564 * 1565 * @return [description] 1566 */ 1567 public boolean getCanNonOwnersInvite() { 1568 return this.canNonOwnersInvite; 1569 } 1570 1571 /** 1572 * Sets whether or not non-owners can invite collaborators to the folder. 1573 * 1574 * @param canNonOwnersInvite indicates non-owners can invite collaborators to the folder. 1575 */ 1576 public void setCanNonOwnersInvite(boolean canNonOwnersInvite) { 1577 this.canNonOwnersInvite = canNonOwnersInvite; 1578 this.addPendingChange("can_non_owners_invite", canNonOwnersInvite); 1579 } 1580 1581 /** 1582 * Gets whether future collaborations should be restricted to within the enterprise only. 1583 * 1584 * @return indicates whether collaboration is restricted to enterprise only. 1585 */ 1586 public boolean getIsCollaborationRestrictedToEnterprise() { 1587 return this.isCollaborationRestrictedToEnterprise; 1588 } 1589 1590 /** 1591 * Sets whether future collaborations should be restricted to within the enterprise only. 1592 * 1593 * @param isRestricted indicates whether there is collaboration restriction within enterprise. 1594 */ 1595 public void setIsCollaborationRestrictedToEnterprise(boolean isRestricted) { 1596 this.isCollaborationRestrictedToEnterprise = isRestricted; 1597 this.addPendingChange("is_collaboration_restricted_to_enterprise", isRestricted); 1598 } 1599 1600 /** 1601 * Retrieves the allowed roles for collaborations. 1602 * 1603 * @return the roles allowed for collaboration. 1604 */ 1605 public List<String> getAllowedInviteeRoles() { 1606 return this.allowedInviteeRoles; 1607 } 1608 1609 /** 1610 * Retrieves the allowed access levels for a shared link. 1611 * 1612 * @return the allowed access levels for a shared link. 1613 */ 1614 public List<String> getAllowedSharedLinkAccessLevels() { 1615 return this.allowedSharedLinkAccessLevels; 1616 } 1617 1618 /** 1619 * Gets flag indicating whether this file is Watermarked. 1620 * 1621 * @return whether the file is watermarked or not 1622 */ 1623 public boolean getIsWatermarked() { 1624 return this.isWatermarked; 1625 } 1626 1627 /** 1628 * Gets the metadata on this folder associated with a specified scope and template. 1629 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. 1630 * 1631 * @param templateName the metadata template type name. 1632 * @param scope the scope of the template (usually "global" or "enterprise"). 1633 * @return the metadata returned from the server. 1634 */ 1635 public Metadata getMetadata(String templateName, String scope) { 1636 try { 1637 return this.metadataMap.get(scope).get(templateName); 1638 } catch (NullPointerException e) { 1639 return null; 1640 } 1641 } 1642 1643 /** 1644 * Get the field is_externally_owned determining whether this folder is owned by a user outside of the 1645 * enterprise. 1646 * 1647 * @return a boolean indicating whether this folder is owned by a user outside the enterprise. 1648 */ 1649 public boolean getIsExternallyOwned() { 1650 return this.isExternallyOwned; 1651 } 1652 1653 /** 1654 * Gets the metadata classification type of this folder. 1655 * 1656 * @return the metadata classification type of this folder. 1657 */ 1658 public BoxClassification getClassification() { 1659 return this.classification; 1660 } 1661 1662 /** 1663 * Returns the flag indicating whether the folder is accessible via a shared link. 1664 * 1665 * @return boolean flag indicating whether the folder is accessible via a shared link. 1666 */ 1667 public boolean getIsAccessibleViaSharedLink() { 1668 return this.isAccessibleViaSharedLink; 1669 } 1670 1671 1672 @Override 1673 public BoxFolder getResource() { 1674 return BoxFolder.this; 1675 } 1676 1677 @Override 1678 protected void parseJSONMember(JsonObject.Member member) { 1679 super.parseJSONMember(member); 1680 1681 String memberName = member.getName(); 1682 JsonValue value = member.getValue(); 1683 try { 1684 switch (memberName) { 1685 case "folder_upload_email": 1686 if (this.uploadEmail == null) { 1687 this.uploadEmail = new BoxUploadEmail(value.asObject()); 1688 } else { 1689 this.uploadEmail.update(value.asObject()); 1690 } 1691 break; 1692 case "has_collaborations": 1693 this.hasCollaborations = value.asBoolean(); 1694 break; 1695 case "sync_state": 1696 this.syncState = SyncState.fromJSONValue(value.asString()); 1697 break; 1698 case "permissions": 1699 this.permissions = this.parsePermissions(value.asObject()); 1700 break; 1701 case "can_non_owners_invite": 1702 this.canNonOwnersInvite = value.asBoolean(); 1703 break; 1704 case "allowed_shared_link_access_levels": 1705 this.allowedSharedLinkAccessLevels = this.parseSharedLinkAccessLevels(value.asArray()); 1706 break; 1707 case "allowed_invitee_roles": 1708 this.allowedInviteeRoles = this.parseAllowedInviteeRoles(value.asArray()); 1709 break; 1710 case "is_collaboration_restricted_to_enterprise": 1711 this.isCollaborationRestrictedToEnterprise = value.asBoolean(); 1712 break; 1713 case "is_externally_owned": 1714 this.isExternallyOwned = value.asBoolean(); 1715 break; 1716 case "watermark_info": 1717 this.isWatermarked = value.asObject().get("is_watermarked").asBoolean(); 1718 break; 1719 case "metadata": 1720 this.metadataMap = Parsers.parseAndPopulateMetadataMap(value.asObject()); 1721 break; 1722 case "classification": 1723 if (value.isNull()) { 1724 this.classification = null; 1725 } else { 1726 this.classification = new BoxClassification(value.asObject()); 1727 } 1728 break; 1729 case "is_accessible_via_shared_link": 1730 this.isAccessibleViaSharedLink = value.asBoolean(); 1731 break; 1732 default: 1733 break; 1734 } 1735 } catch (Exception e) { 1736 throw new BoxDeserializationException(memberName, value.toString(), e); 1737 } 1738 } 1739 1740 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1741 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1742 for (JsonObject.Member member : jsonObject) { 1743 JsonValue value = member.getValue(); 1744 if (value.isNull() || !value.asBoolean()) { 1745 continue; 1746 } 1747 1748 try { 1749 permissions.add(BoxFolder.Permission.fromJSONValue(member.getName())); 1750 } catch (IllegalArgumentException ignored) { 1751 // If the permission is not recognized, we ignore it. 1752 } 1753 } 1754 1755 return permissions; 1756 } 1757 1758 private List<String> parseSharedLinkAccessLevels(JsonArray jsonArray) { 1759 List<String> accessLevels = new ArrayList<>(jsonArray.size()); 1760 for (JsonValue value : jsonArray) { 1761 accessLevels.add(value.asString()); 1762 } 1763 1764 return accessLevels; 1765 } 1766 1767 private List<String> parseAllowedInviteeRoles(JsonArray jsonArray) { 1768 List<String> roles = new ArrayList<>(jsonArray.size()); 1769 for (JsonValue value : jsonArray) { 1770 roles.add(value.asString()); 1771 } 1772 1773 return roles; 1774 } 1775 } 1776}