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