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