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