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