001package com.box.sdk; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.net.URL; 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.Date; 009import java.util.EnumSet; 010import java.util.Iterator; 011import java.util.Map; 012import java.util.concurrent.TimeUnit; 013 014import com.box.sdk.internal.utils.Parsers; 015import com.eclipsesource.json.JsonArray; 016import com.eclipsesource.json.JsonObject; 017import com.eclipsesource.json.JsonValue; 018 019/** 020 * Represents a folder on Box. This class can be used to iterate through a folder's contents, collaborate a folder with 021 * another user or group, and perform other common folder operations (move, copy, delete, etc.). 022 * <p> 023 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 024 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 025 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p> 026 */ 027@BoxResourceType("folder") 028public class BoxFolder extends BoxItem implements Iterable<BoxItem.Info> { 029 /** 030 * An array of all possible folder fields that can be requested when calling {@link #getInfo()}. 031 */ 032 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "name", "created_at", "modified_at", 033 "description", "size", "path_collection", "created_by", "modified_by", "trashed_at", "purged_at", 034 "content_created_at", "content_modified_at", "owned_by", "shared_link", "folder_upload_email", "parent", 035 "item_status", "item_collection", "sync_state", "has_collaborations", "permissions", "tags", 036 "can_non_owners_invite", "collections", "watermark_info", "metadata"}; 037 038 /** 039 * Create Folder URL Template. 040 */ 041 public static final URLTemplate CREATE_FOLDER_URL = new URLTemplate("folders"); 042 /** 043 * Create Web Link URL Template. 044 */ 045 public static final URLTemplate CREATE_WEB_LINK_URL = new URLTemplate("web_links"); 046 /** 047 * Copy Folder URL Template. 048 */ 049 public static final URLTemplate COPY_FOLDER_URL = new URLTemplate("folders/%s/copy"); 050 /** 051 * Delete Folder URL Template. 052 */ 053 public static final URLTemplate DELETE_FOLDER_URL = new URLTemplate("folders/%s?recursive=%b"); 054 /** 055 * Folder Info URL Template. 056 */ 057 public static final URLTemplate FOLDER_INFO_URL_TEMPLATE = new URLTemplate("folders/%s"); 058 /** 059 * Upload File URL Template. 060 */ 061 public static final URLTemplate UPLOAD_FILE_URL = new URLTemplate("files/content"); 062 /** 063 * Add Collaboration URL Template. 064 */ 065 public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations"); 066 /** 067 * Get Collaborations URL Template. 068 */ 069 public static final URLTemplate GET_COLLABORATIONS_URL = new URLTemplate("folders/%s/collaborations"); 070 /** 071 * Get Items URL Template. 072 */ 073 public static final URLTemplate GET_ITEMS_URL = new URLTemplate("folders/%s/items/"); 074 /** 075 * Search URL Template. 076 */ 077 public static final URLTemplate SEARCH_URL_TEMPLATE = new URLTemplate("search"); 078 /** 079 * Metadata URL Template. 080 */ 081 public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("folders/%s/metadata/%s/%s"); 082 /** 083 * Upload Session URL Template. 084 */ 085 public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions"); 086 087 /** 088 * Constructs a BoxFolder for a folder with a given ID. 089 * 090 * @param api the API connection to be used by the folder. 091 * @param id the ID of the folder. 092 */ 093 public BoxFolder(BoxAPIConnection api, String id) { 094 super(api, id); 095 } 096 097 /** 098 * {@inheritDoc} 099 */ 100 @Override 101 protected URL getItemURL() { 102 return FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 103 } 104 105 /** 106 * Gets the current user's root folder. 107 * 108 * @param api the API connection to be used by the folder. 109 * @return the user's root folder. 110 */ 111 public static BoxFolder getRootFolder(BoxAPIConnection api) { 112 return new BoxFolder(api, "0"); 113 } 114 115 /** 116 * Adds a collaborator to this folder. 117 * 118 * @param collaborator the collaborator to add. 119 * @param role the role of the collaborator. 120 * @return info about the new collaboration. 121 */ 122 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role) { 123 JsonObject accessibleByField = new JsonObject(); 124 accessibleByField.add("id", collaborator.getID()); 125 126 if (collaborator instanceof BoxUser) { 127 accessibleByField.add("type", "user"); 128 } else if (collaborator instanceof BoxGroup) { 129 accessibleByField.add("type", "group"); 130 } else { 131 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 132 } 133 134 return this.collaborate(accessibleByField, role, null, null); 135 } 136 137 /** 138 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 139 * account. 140 * 141 * @param email the email address of the collaborator to add. 142 * @param role the role of the collaborator. 143 * @return info about the new collaboration. 144 */ 145 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role) { 146 JsonObject accessibleByField = new JsonObject(); 147 accessibleByField.add("login", email); 148 accessibleByField.add("type", "user"); 149 150 return this.collaborate(accessibleByField, role, null, null); 151 } 152 153 /** 154 * Adds a collaborator to this folder. 155 * 156 * @param collaborator the collaborator to add. 157 * @param role the role of the collaborator. 158 * @param notify the user/group should receive email notification of the collaboration or not. 159 * @param canViewPath the view path collaboration feature is enabled or not. 160 * View path collaborations allow the invitee to see the entire ancestral path to the associated 161 * folder. The user will not gain privileges in any ancestral folder. 162 * @return info about the new collaboration. 163 */ 164 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 165 Boolean notify, Boolean canViewPath) { 166 JsonObject accessibleByField = new JsonObject(); 167 accessibleByField.add("id", collaborator.getID()); 168 169 if (collaborator instanceof BoxUser) { 170 accessibleByField.add("type", "user"); 171 } else if (collaborator instanceof BoxGroup) { 172 accessibleByField.add("type", "group"); 173 } else { 174 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 175 } 176 177 return this.collaborate(accessibleByField, role, notify, canViewPath); 178 } 179 180 /** 181 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 182 * account. 183 * 184 * @param email the email address of the collaborator to add. 185 * @param role the role of the collaborator. 186 * @param notify the user/group should receive email notification of the collaboration or not. 187 * @param canViewPath the view path collaboration feature is enabled or not. 188 * View path collaborations allow the invitee to see the entire ancestral path to the associated 189 * folder. The user will not gain privileges in any ancestral folder. 190 * @return info about the new collaboration. 191 */ 192 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 193 Boolean notify, Boolean canViewPath) { 194 JsonObject accessibleByField = new JsonObject(); 195 accessibleByField.add("login", email); 196 accessibleByField.add("type", "user"); 197 198 return this.collaborate(accessibleByField, role, notify, canViewPath); 199 } 200 201 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 202 Boolean notify, Boolean canViewPath) { 203 204 JsonObject itemField = new JsonObject(); 205 itemField.add("id", this.getID()); 206 itemField.add("type", "folder"); 207 208 return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath); 209 } 210 211 @Override 212 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 213 BoxSharedLink.Permissions permissions) { 214 215 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions); 216 Info info = new Info(); 217 info.setSharedLink(sharedLink); 218 219 this.updateInfo(info); 220 return info.getSharedLink(); 221 } 222 223 /** 224 * Creates new SharedLink for a BoxFolder with a password. 225 * 226 * @param access The access level of the shared link. 227 * @param unshareDate A specified date to unshare the Box folder. 228 * @param permissions The permissions to set on the shared link for the Box folder. 229 * @param password Password set on the shared link to give access to the Box folder. 230 * @return information about the newly created shared link. 231 */ 232 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 233 BoxSharedLink.Permissions permissions, String password) { 234 235 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions, password); 236 Info info = new Info(); 237 info.setSharedLink(sharedLink); 238 239 this.updateInfo(info); 240 return info.getSharedLink(); 241 } 242 243 /** 244 * Gets information about all of the collaborations for this folder. 245 * 246 * @return a collection of information about the collaborations for this folder. 247 */ 248 public Collection<BoxCollaboration.Info> getCollaborations() { 249 BoxAPIConnection api = this.getAPI(); 250 URL url = GET_COLLABORATIONS_URL.build(api.getBaseURL(), this.getID()); 251 252 BoxAPIRequest request = new BoxAPIRequest(api, url, "GET"); 253 BoxJSONResponse response = (BoxJSONResponse) request.send(); 254 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 255 256 int entriesCount = responseJSON.get("total_count").asInt(); 257 Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount); 258 JsonArray entries = responseJSON.get("entries").asArray(); 259 for (JsonValue entry : entries) { 260 JsonObject entryObject = entry.asObject(); 261 BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString()); 262 BoxCollaboration.Info info = collaboration.new Info(entryObject); 263 collaborations.add(info); 264 } 265 266 return collaborations; 267 } 268 269 @Override 270 public BoxFolder.Info getInfo() { 271 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 272 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 273 BoxJSONResponse response = (BoxJSONResponse) request.send(); 274 return new Info(response.getJSON()); 275 } 276 277 @Override 278 public BoxFolder.Info getInfo(String... fields) { 279 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 280 URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 281 282 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 283 BoxJSONResponse response = (BoxJSONResponse) request.send(); 284 return new Info(response.getJSON()); 285 } 286 287 /** 288 * Updates the information about this folder with any info fields that have been modified locally. 289 * 290 * @param info the updated info. 291 */ 292 public void updateInfo(BoxFolder.Info info) { 293 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 294 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 295 request.setBody(info.getPendingChanges()); 296 BoxJSONResponse response = (BoxJSONResponse) request.send(); 297 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 298 info.update(jsonObject); 299 } 300 301 @Override 302 public BoxFolder.Info copy(BoxFolder destination) { 303 return this.copy(destination, null); 304 } 305 306 @Override 307 public BoxFolder.Info copy(BoxFolder destination, String newName) { 308 URL url = COPY_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID()); 309 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 310 311 JsonObject parent = new JsonObject(); 312 parent.add("id", destination.getID()); 313 314 JsonObject copyInfo = new JsonObject(); 315 copyInfo.add("parent", parent); 316 if (newName != null) { 317 copyInfo.add("name", newName); 318 } 319 320 request.setBody(copyInfo.toString()); 321 BoxJSONResponse response = (BoxJSONResponse) request.send(); 322 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 323 BoxFolder copiedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 324 return copiedFolder.new Info(responseJSON); 325 } 326 327 /** 328 * Creates a new child folder inside this folder. 329 * 330 * @param name the new folder's name. 331 * @return the created folder's info. 332 */ 333 public BoxFolder.Info createFolder(String name) { 334 JsonObject parent = new JsonObject(); 335 parent.add("id", this.getID()); 336 337 JsonObject newFolder = new JsonObject(); 338 newFolder.add("name", name); 339 newFolder.add("parent", parent); 340 341 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), CREATE_FOLDER_URL.build(this.getAPI().getBaseURL()), 342 "POST"); 343 request.setBody(newFolder.toString()); 344 BoxJSONResponse response = (BoxJSONResponse) request.send(); 345 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 346 347 BoxFolder createdFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 348 return createdFolder.new Info(responseJSON); 349 } 350 351 /** 352 * Deletes this folder, optionally recursively deleting all of its contents. 353 * 354 * @param recursive true to recursively delete this folder's contents; otherwise false. 355 */ 356 public void delete(boolean recursive) { 357 URL url = DELETE_FOLDER_URL.build(this.getAPI().getBaseURL(), this.getID(), recursive); 358 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 359 BoxAPIResponse response = request.send(); 360 response.disconnect(); 361 } 362 363 @Override 364 public BoxItem.Info move(BoxFolder destination) { 365 return this.move(destination, null); 366 } 367 368 @Override 369 public BoxItem.Info move(BoxFolder destination, String newName) { 370 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 371 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 372 373 JsonObject parent = new JsonObject(); 374 parent.add("id", destination.getID()); 375 376 JsonObject updateInfo = new JsonObject(); 377 updateInfo.add("parent", parent); 378 if (newName != null) { 379 updateInfo.add("name", newName); 380 } 381 382 request.setBody(updateInfo.toString()); 383 BoxJSONResponse response = (BoxJSONResponse) request.send(); 384 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 385 BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString()); 386 return movedFolder.new Info(responseJSON); 387 } 388 389 /** 390 * Renames this folder. 391 * 392 * @param newName the new name of the folder. 393 */ 394 public void rename(String newName) { 395 URL url = FOLDER_INFO_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 396 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 397 398 JsonObject updateInfo = new JsonObject(); 399 updateInfo.add("name", newName); 400 401 request.setBody(updateInfo.toString()); 402 BoxJSONResponse response = (BoxJSONResponse) request.send(); 403 response.getJSON(); 404 } 405 406 /** 407 * Checks if the file can be successfully uploaded by using the preflight check. 408 * 409 * @param name the name to give the uploaded file. 410 * @param fileSize the size of the file used for account capacity calculations. 411 */ 412 public void canUpload(String name, long fileSize) { 413 URL url = UPLOAD_FILE_URL.build(this.getAPI().getBaseURL()); 414 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 415 416 JsonObject parent = new JsonObject(); 417 parent.add("id", this.getID()); 418 419 JsonObject preflightInfo = new JsonObject(); 420 preflightInfo.add("parent", parent); 421 preflightInfo.add("name", name); 422 423 preflightInfo.add("size", fileSize); 424 425 request.setBody(preflightInfo.toString()); 426 BoxAPIResponse response = request.send(); 427 response.disconnect(); 428 } 429 430 /** 431 * Uploads a new file to this folder. 432 * 433 * @param fileContent a stream containing the contents of the file to upload. 434 * @param name the name to give the uploaded file. 435 * @return the uploaded file's info. 436 */ 437 public BoxFile.Info uploadFile(InputStream fileContent, String name) { 438 FileUploadParams uploadInfo = new FileUploadParams() 439 .setContent(fileContent) 440 .setName(name); 441 return this.uploadFile(uploadInfo); 442 } 443 444 /** 445 * Uploads a new file to this folder while reporting the progress to a ProgressListener. 446 * 447 * @param fileContent a stream containing the contents of the file to upload. 448 * @param name the name to give the uploaded file. 449 * @param fileSize the size of the file used for determining the progress of the upload. 450 * @param listener a listener for monitoring the upload's progress. 451 * @return the uploaded file's info. 452 */ 453 public BoxFile.Info uploadFile(InputStream fileContent, String name, long fileSize, ProgressListener listener) { 454 FileUploadParams uploadInfo = new FileUploadParams() 455 .setContent(fileContent) 456 .setName(name) 457 .setSize(fileSize) 458 .setProgressListener(listener); 459 return this.uploadFile(uploadInfo); 460 } 461 462 /** 463 * Uploads a new file to this folder with custom upload parameters. 464 * 465 * @param uploadParams the custom upload parameters. 466 * @return the uploaded file's info. 467 */ 468 public BoxFile.Info uploadFile(FileUploadParams uploadParams) { 469 URL uploadURL = UPLOAD_FILE_URL.build(this.getAPI().getBaseUploadURL()); 470 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 471 472 JsonObject fieldJSON = new JsonObject(); 473 JsonObject parentIdJSON = new JsonObject(); 474 parentIdJSON.add("id", getID()); 475 fieldJSON.add("name", uploadParams.getName()); 476 fieldJSON.add("parent", parentIdJSON); 477 478 if (uploadParams.getCreated() != null) { 479 fieldJSON.add("content_created_at", BoxDateFormat.format(uploadParams.getCreated())); 480 } 481 482 if (uploadParams.getModified() != null) { 483 fieldJSON.add("content_modified_at", BoxDateFormat.format(uploadParams.getModified())); 484 } 485 486 if (uploadParams.getSHA1() != null && !uploadParams.getSHA1().isEmpty()) { 487 request.setContentSHA1(uploadParams.getSHA1()); 488 } 489 490 request.putField("attributes", fieldJSON.toString()); 491 492 if (uploadParams.getSize() > 0) { 493 request.setFile(uploadParams.getContent(), uploadParams.getName(), uploadParams.getSize()); 494 } else { 495 request.setFile(uploadParams.getContent(), uploadParams.getName()); 496 } 497 498 BoxJSONResponse response; 499 if (uploadParams.getProgressListener() == null) { 500 response = (BoxJSONResponse) request.send(); 501 } else { 502 response = (BoxJSONResponse) request.send(uploadParams.getProgressListener()); 503 } 504 JsonObject collection = JsonObject.readFrom(response.getJSON()); 505 JsonArray entries = collection.get("entries").asArray(); 506 JsonObject fileInfoJSON = entries.get(0).asObject(); 507 String uploadedFileID = fileInfoJSON.get("id").asString(); 508 509 BoxFile uploadedFile = new BoxFile(getAPI(), uploadedFileID); 510 return uploadedFile.new Info(fileInfoJSON); 511 } 512 513 /** 514 * Uploads a new weblink to this folder. 515 * 516 * @param linkURL the URL the weblink points to. 517 * @return the uploaded weblink's info. 518 */ 519 public BoxWebLink.Info createWebLink(URL linkURL) { 520 return this.createWebLink(null, linkURL, 521 null); 522 } 523 524 /** 525 * Uploads a new weblink to this folder. 526 * 527 * @param name the filename for the weblink. 528 * @param linkURL the URL the weblink points to. 529 * @return the uploaded weblink's info. 530 */ 531 public BoxWebLink.Info createWebLink(String name, URL linkURL) { 532 return this.createWebLink(name, linkURL, 533 null); 534 } 535 536 /** 537 * Uploads a new weblink to this folder. 538 * 539 * @param linkURL the URL the weblink points to. 540 * @param description the weblink's description. 541 * @return the uploaded weblink's info. 542 */ 543 public BoxWebLink.Info createWebLink(URL linkURL, String description) { 544 return this.createWebLink(null, linkURL, description); 545 } 546 547 /** 548 * Uploads a new weblink to this folder. 549 * 550 * @param name the filename for the weblink. 551 * @param linkURL the URL the weblink points to. 552 * @param description the weblink's description. 553 * @return the uploaded weblink's info. 554 */ 555 public BoxWebLink.Info createWebLink(String name, URL linkURL, String description) { 556 JsonObject parent = new JsonObject(); 557 parent.add("id", this.getID()); 558 559 JsonObject newWebLink = new JsonObject(); 560 newWebLink.add("name", name); 561 newWebLink.add("parent", parent); 562 newWebLink.add("url", linkURL.toString()); 563 564 if (description != null) { 565 newWebLink.add("description", description); 566 } 567 568 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), 569 CREATE_WEB_LINK_URL.build(this.getAPI().getBaseURL()), "POST"); 570 request.setBody(newWebLink.toString()); 571 BoxJSONResponse response = (BoxJSONResponse) request.send(); 572 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 573 574 BoxWebLink createdWebLink = new BoxWebLink(this.getAPI(), responseJSON.get("id").asString()); 575 return createdWebLink.new Info(responseJSON); 576 } 577 578 /** 579 * Returns an iterable containing the items in this folder. Iterating over the iterable returned by this method is 580 * equivalent to iterating over this BoxFolder directly. 581 * 582 * @return an iterable containing the items in this folder. 583 */ 584 public Iterable<BoxItem.Info> getChildren() { 585 return this; 586 } 587 588 /** 589 * Returns an iterable containing the items in this folder and specifies which child fields to retrieve from the 590 * API. 591 * 592 * @param fields the fields to retrieve. 593 * @return an iterable containing the items in this folder. 594 */ 595 public Iterable<BoxItem.Info> getChildren(final String... fields) { 596 return new Iterable<BoxItem.Info>() { 597 @Override 598 public Iterator<BoxItem.Info> iterator() { 599 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 600 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), queryString, getID()); 601 return new BoxItemIterator(getAPI(), url); 602 } 603 }; 604 } 605 606 /** 607 * Retrieves a specific range of child items in this folder. 608 * 609 * @param offset the index of the first child item to retrieve. 610 * @param limit the maximum number of children to retrieve after the offset. 611 * @param fields the fields to retrieve. 612 * @return a partial collection containing the specified range of child items. 613 */ 614 public PartialCollection<BoxItem.Info> getChildrenRange(long offset, long limit, String... fields) { 615 QueryStringBuilder builder = new QueryStringBuilder() 616 .appendParam("limit", limit) 617 .appendParam("offset", offset); 618 619 if (fields.length > 0) { 620 builder.appendParam("fields", fields).toString(); 621 } 622 623 URL url = GET_ITEMS_URL.buildWithQuery(getAPI().getBaseURL(), builder.toString(), getID()); 624 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 625 BoxJSONResponse response = (BoxJSONResponse) request.send(); 626 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 627 628 String totalCountString = responseJSON.get("total_count").toString(); 629 long fullSize = Double.valueOf(totalCountString).longValue(); 630 PartialCollection<BoxItem.Info> children = new PartialCollection<BoxItem.Info>(offset, limit, fullSize); 631 JsonArray jsonArray = responseJSON.get("entries").asArray(); 632 for (JsonValue value : jsonArray) { 633 JsonObject jsonObject = value.asObject(); 634 BoxItem.Info parsedItemInfo = (BoxItem.Info) BoxResource.parseInfo(this.getAPI(), jsonObject); 635 if (parsedItemInfo != null) { 636 children.add(parsedItemInfo); 637 } 638 } 639 return children; 640 } 641 642 /** 643 * Returns an iterator over the items in this folder. 644 * 645 * @return an iterator over the items in this folder. 646 */ 647 @Override 648 public Iterator<BoxItem.Info> iterator() { 649 URL url = GET_ITEMS_URL.build(this.getAPI().getBaseURL(), BoxFolder.this.getID()); 650 return new BoxItemIterator(BoxFolder.this.getAPI(), url); 651 } 652 653 /** 654 * Adds new {@link BoxWebHook} to this {@link BoxFolder}. 655 * 656 * @param address {@link BoxWebHook.Info#getAddress()} 657 * @param triggers {@link BoxWebHook.Info#getTriggers()} 658 * @return created {@link BoxWebHook.Info} 659 */ 660 public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) { 661 return BoxWebHook.create(this, address, triggers); 662 } 663 664 /** 665 * Used to retrieve the watermark for the folder. 666 * If the folder does not have a watermark applied to it, a 404 Not Found will be returned by API. 667 * 668 * @param fields the fields to retrieve. 669 * @return the watermark associated with the folder. 670 */ 671 public BoxWatermark getWatermark(String... fields) { 672 return this.getWatermark(FOLDER_INFO_URL_TEMPLATE, fields); 673 } 674 675 /** 676 * Used to apply or update the watermark for the folder. 677 * 678 * @return the watermark associated with the folder. 679 */ 680 public BoxWatermark applyWatermark() { 681 return this.applyWatermark(FOLDER_INFO_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 682 } 683 684 /** 685 * Removes a watermark from the folder. 686 * If the folder did not have a watermark applied to it, a 404 Not Found will be returned by API. 687 */ 688 public void removeWatermark() { 689 this.removeWatermark(FOLDER_INFO_URL_TEMPLATE); 690 } 691 692 /** 693 * Used to retrieve all metadata associated with the folder. 694 * 695 * @param fields the optional fields to retrieve. 696 * @return An iterable of metadata instances associated with the folder 697 */ 698 public Iterable<Metadata> getAllMetadata(String... fields) { 699 return Metadata.getAllMetadata(this, fields); 700 } 701 702 /** 703 * This method is deprecated, please use the {@link BoxSearch} class instead. 704 * Searches this folder and all descendant folders using a given queryPlease use BoxSearch Instead. 705 * 706 * @param query the search query. 707 * @return an Iterable containing the search results. 708 */ 709 @Deprecated 710 public Iterable<BoxItem.Info> search(final String query) { 711 return new Iterable<BoxItem.Info>() { 712 @Override 713 public Iterator<BoxItem.Info> iterator() { 714 QueryStringBuilder builder = new QueryStringBuilder(); 715 builder.appendParam("query", query); 716 builder.appendParam("ancestor_folder_ids", getID()); 717 718 URL url = SEARCH_URL_TEMPLATE.buildWithQuery(getAPI().getBaseURL(), builder.toString()); 719 return new BoxItemIterator(getAPI(), url); 720 } 721 }; 722 } 723 724 @Override 725 public BoxFolder.Info setCollections(BoxCollection... collections) { 726 JsonArray jsonArray = new JsonArray(); 727 for (BoxCollection collection : collections) { 728 JsonObject collectionJSON = new JsonObject(); 729 collectionJSON.add("id", collection.getID()); 730 jsonArray.add(collectionJSON); 731 } 732 JsonObject infoJSON = new JsonObject(); 733 infoJSON.add("collections", jsonArray); 734 735 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 736 URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 737 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 738 request.setBody(infoJSON.toString()); 739 BoxJSONResponse response = (BoxJSONResponse) request.send(); 740 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 741 return new Info(jsonObject); 742 } 743 744 /** 745 * Creates global property metadata on this folder. 746 * 747 * @param metadata the new metadata values. 748 * @return the metadata returned from the server. 749 */ 750 public Metadata createMetadata(Metadata metadata) { 751 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 752 } 753 754 /** 755 * Creates metadata on this folder using a specified template. 756 * 757 * @param templateName the name of the metadata template. 758 * @param metadata the new metadata values. 759 * @return the metadata returned from the server. 760 */ 761 public Metadata createMetadata(String templateName, Metadata metadata) { 762 String scope = Metadata.scopeBasedOnType(templateName); 763 return this.createMetadata(templateName, scope, metadata); 764 } 765 766 /** 767 * Creates metadata on this folder using a specified scope and template. 768 * 769 * @param templateName the name of the metadata template. 770 * @param scope the scope of the template (usually "global" or "enterprise"). 771 * @param metadata the new metadata values. 772 * @return the metadata returned from the server. 773 */ 774 public Metadata createMetadata(String templateName, String scope, Metadata metadata) { 775 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 776 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST"); 777 request.addHeader("Content-Type", "application/json"); 778 request.setBody(metadata.toString()); 779 BoxJSONResponse response = (BoxJSONResponse) request.send(); 780 return new Metadata(JsonObject.readFrom(response.getJSON())); 781 } 782 783 /** 784 * Gets the global properties metadata on this folder. 785 * 786 * @return the metadata returned from the server. 787 */ 788 public Metadata getMetadata() { 789 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 790 } 791 792 /** 793 * Gets the metadata on this folder associated with a specified template. 794 * 795 * @param templateName the metadata template type name. 796 * @return the metadata returned from the server. 797 */ 798 public Metadata getMetadata(String templateName) { 799 String scope = Metadata.scopeBasedOnType(templateName); 800 return this.getMetadata(templateName, scope); 801 } 802 803 /** 804 * Gets the metadata on this folder associated with a specified scope and template. 805 * 806 * @param templateName the metadata template type name. 807 * @param scope the scope of the template (usually "global" or "enterprise"). 808 * @return the metadata returned from the server. 809 */ 810 public Metadata getMetadata(String templateName, String scope) { 811 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 812 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 813 BoxJSONResponse response = (BoxJSONResponse) request.send(); 814 return new Metadata(JsonObject.readFrom(response.getJSON())); 815 } 816 817 /** 818 * Updates the global properties metadata on this folder. 819 * 820 * @param metadata the new metadata values. 821 * @return the metadata returned from the server. 822 */ 823 public Metadata updateMetadata(Metadata metadata) { 824 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), metadata.getScope(), 825 metadata.getTemplateName()); 826 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 827 request.addHeader("Content-Type", "application/json-patch+json"); 828 request.setBody(metadata.getPatch()); 829 BoxJSONResponse response = (BoxJSONResponse) request.send(); 830 return new Metadata(JsonObject.readFrom(response.getJSON())); 831 } 832 833 /** 834 * Deletes the global properties metadata on this folder. 835 */ 836 public void deleteMetadata() { 837 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 838 } 839 840 /** 841 * Deletes the metadata on this folder associated with a specified template. 842 * 843 * @param templateName the metadata template type name. 844 */ 845 public void deleteMetadata(String templateName) { 846 String scope = Metadata.scopeBasedOnType(templateName); 847 this.deleteMetadata(templateName, scope); 848 } 849 850 /** 851 * Deletes the metadata on this folder associated with a specified scope and template. 852 * 853 * @param templateName the metadata template type name. 854 * @param scope the scope of the template (usually "global" or "enterprise"). 855 */ 856 public void deleteMetadata(String templateName, String scope) { 857 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, templateName); 858 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 859 BoxAPIResponse response = request.send(); 860 response.disconnect(); 861 } 862 863 /** 864 * Creates an upload session to create a new file in chunks. 865 * This will first verify that the file can be created and then open a session for uploading pieces of the file. 866 * 867 * @param fileName the name of the file to be created 868 * @param fileSize the size of the file that will be uploaded 869 * @return the created upload session instance 870 */ 871 public BoxFileUploadSession.Info createUploadSession(String fileName, long fileSize) { 872 873 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 874 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 875 876 JsonObject body = new JsonObject(); 877 body.add("folder_id", this.getID()); 878 body.add("file_name", fileName); 879 body.add("file_size", fileSize); 880 request.setBody(body.toString()); 881 882 BoxJSONResponse response = (BoxJSONResponse) request.send(); 883 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 884 885 String sessionId = jsonObject.get("id").asString(); 886 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 887 888 return session.new Info(jsonObject); 889 } 890 891 /** 892 * Creates a new file. 893 * 894 * @param inputStream the stream instance that contains the data. 895 * @param fileName the name of the file to be created. 896 * @param fileSize the size of the file that will be uploaded. 897 * @return the created file instance. 898 * @throws InterruptedException when a thread execution is interrupted. 899 * @throws IOException when reading a stream throws exception. 900 */ 901 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize) 902 throws InterruptedException, IOException { 903 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 904 return new LargeFileUpload(). 905 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize); 906 } 907 908 /** 909 * Creates a new file using specified number of parallel http connections. 910 * 911 * @param inputStream the stream instance that contains the data. 912 * @param fileName the name of the file to be created. 913 * @param fileSize the size of the file that will be uploaded. 914 * @param nParallelConnections number of parallel http connections to use 915 * @param timeOut time to wait before killing the job 916 * @param unit time unit for the time wait value 917 * @return the created file instance. 918 * @throws InterruptedException when a thread execution is interrupted. 919 * @throws IOException when reading a stream throws exception. 920 */ 921 public BoxFile.Info uploadLargeFile(InputStream inputStream, String fileName, long fileSize, 922 int nParallelConnections, long timeOut, TimeUnit unit) 923 throws InterruptedException, IOException { 924 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL()); 925 return new LargeFileUpload(nParallelConnections, timeOut, unit). 926 upload(this.getAPI(), this.getID(), inputStream, url, fileName, fileSize); 927 } 928 929 /** 930 * Contains information about a BoxFolder. 931 */ 932 public class Info extends BoxItem.Info { 933 private BoxUploadEmail uploadEmail; 934 private boolean hasCollaborations; 935 private SyncState syncState; 936 private EnumSet<Permission> permissions; 937 private boolean canNonOwnersInvite; 938 private boolean isWatermarked; 939 private Map<String, Map<String, Metadata>> metadataMap; 940 941 /** 942 * Constructs an empty Info object. 943 */ 944 public Info() { 945 super(); 946 } 947 948 /** 949 * Constructs an Info object by parsing information from a JSON string. 950 * 951 * @param json the JSON string to parse. 952 */ 953 public Info(String json) { 954 super(json); 955 } 956 957 /** 958 * Constructs an Info object using an already parsed JSON object. 959 * 960 * @param jsonObject the parsed JSON object. 961 */ 962 public Info(JsonObject jsonObject) { 963 super(jsonObject); 964 } 965 966 /** 967 * Gets the upload email for the folder. 968 * 969 * @return the upload email for the folder. 970 */ 971 public BoxUploadEmail getUploadEmail() { 972 return this.uploadEmail; 973 } 974 975 /** 976 * Sets the upload email for the folder. 977 * 978 * @param uploadEmail the upload email for the folder. 979 */ 980 public void setUploadEmail(BoxUploadEmail uploadEmail) { 981 if (this.uploadEmail == uploadEmail) { 982 return; 983 } 984 985 this.removeChildObject("folder_upload_email"); 986 this.uploadEmail = uploadEmail; 987 988 if (uploadEmail == null) { 989 this.addPendingChange("folder_upload_email", (String) null); 990 } else { 991 this.addChildObject("folder_upload_email", uploadEmail); 992 } 993 } 994 995 /** 996 * Gets whether or not the folder has any collaborations. 997 * 998 * @return true if the folder has collaborations; otherwise false. 999 */ 1000 public boolean getHasCollaborations() { 1001 return this.hasCollaborations; 1002 } 1003 1004 /** 1005 * Gets the sync state of the folder. 1006 * 1007 * @return the sync state of the folder. 1008 */ 1009 public SyncState getSyncState() { 1010 return this.syncState; 1011 } 1012 1013 /** 1014 * Sets the sync state of the folder. 1015 * 1016 * @param syncState the sync state of the folder. 1017 */ 1018 public void setSyncState(SyncState syncState) { 1019 this.syncState = syncState; 1020 this.addPendingChange("sync_state", syncState.toJSONValue()); 1021 } 1022 1023 /** 1024 * Gets the permissions that the current user has on the folder. 1025 * 1026 * @return the permissions that the current user has on the folder. 1027 */ 1028 public EnumSet<Permission> getPermissions() { 1029 return this.permissions; 1030 } 1031 1032 /** 1033 * Gets whether or not the non-owners can invite collaborators to the folder. 1034 * 1035 * @return [description] 1036 */ 1037 public boolean getCanNonOwnersInvite() { 1038 return this.canNonOwnersInvite; 1039 } 1040 1041 /** 1042 * Gets flag indicating whether this file is Watermarked. 1043 * 1044 * @return whether the file is watermarked or not 1045 */ 1046 public boolean getIsWatermarked() { 1047 return this.isWatermarked; 1048 } 1049 1050 /** 1051 * Gets the metadata on this folder associated with a specified scope and template. 1052 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found 1053 * then makes an API call to get metadata 1054 * 1055 * @param templateName the metadata template type name. 1056 * @param scope the scope of the template (usually "global" or "enterprise"). 1057 * @return the metadata returned from the server. 1058 */ 1059 public Metadata getMetadata(String templateName, String scope) { 1060 try { 1061 return this.metadataMap.get(scope).get(templateName); 1062 } catch (NullPointerException e) { 1063 return null; 1064 } 1065 } 1066 1067 @Override 1068 public BoxFolder getResource() { 1069 return BoxFolder.this; 1070 } 1071 1072 @Override 1073 protected void parseJSONMember(JsonObject.Member member) { 1074 super.parseJSONMember(member); 1075 1076 String memberName = member.getName(); 1077 JsonValue value = member.getValue(); 1078 if (memberName.equals("folder_upload_email")) { 1079 if (this.uploadEmail == null) { 1080 this.uploadEmail = new BoxUploadEmail(value.asObject()); 1081 } else { 1082 this.uploadEmail.update(value.asObject()); 1083 } 1084 1085 } else if (memberName.equals("has_collaborations")) { 1086 this.hasCollaborations = value.asBoolean(); 1087 1088 } else if (memberName.equals("sync_state")) { 1089 this.syncState = SyncState.fromJSONValue(value.asString()); 1090 1091 } else if (memberName.equals("permissions")) { 1092 this.permissions = this.parsePermissions(value.asObject()); 1093 1094 } else if (memberName.equals("can_non_owners_invite")) { 1095 this.canNonOwnersInvite = value.asBoolean(); 1096 } else if (memberName.equals("watermark_info")) { 1097 JsonObject jsonObject = value.asObject(); 1098 this.isWatermarked = jsonObject.get("is_watermarked").asBoolean(); 1099 } else if (memberName.equals("metadata")) { 1100 JsonObject jsonObject = value.asObject(); 1101 this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject); 1102 } 1103 } 1104 1105 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1106 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1107 for (JsonObject.Member member : jsonObject) { 1108 JsonValue value = member.getValue(); 1109 if (value.isNull() || !value.asBoolean()) { 1110 continue; 1111 } 1112 1113 String memberName = member.getName(); 1114 if (memberName.equals("can_download")) { 1115 permissions.add(Permission.CAN_DOWNLOAD); 1116 } else if (memberName.equals("can_upload")) { 1117 permissions.add(Permission.CAN_UPLOAD); 1118 } else if (memberName.equals("can_rename")) { 1119 permissions.add(Permission.CAN_RENAME); 1120 } else if (memberName.equals("can_delete")) { 1121 permissions.add(Permission.CAN_DELETE); 1122 } else if (memberName.equals("can_share")) { 1123 permissions.add(Permission.CAN_SHARE); 1124 } else if (memberName.equals("can_invite_collaborator")) { 1125 permissions.add(Permission.CAN_INVITE_COLLABORATOR); 1126 } else if (memberName.equals("can_set_share_access")) { 1127 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 1128 } 1129 } 1130 1131 return permissions; 1132 } 1133 } 1134 1135 /** 1136 * Enumerates the possible sync states that a folder can have. 1137 */ 1138 public enum SyncState { 1139 /** 1140 * The folder is synced. 1141 */ 1142 SYNCED("synced"), 1143 1144 /** 1145 * The folder is not synced. 1146 */ 1147 NOT_SYNCED("not_synced"), 1148 1149 /** 1150 * The folder is partially synced. 1151 */ 1152 PARTIALLY_SYNCED("partially_synced"); 1153 1154 private final String jsonValue; 1155 1156 private SyncState(String jsonValue) { 1157 this.jsonValue = jsonValue; 1158 } 1159 1160 static SyncState fromJSONValue(String jsonValue) { 1161 return SyncState.valueOf(jsonValue.toUpperCase()); 1162 } 1163 1164 String toJSONValue() { 1165 return this.jsonValue; 1166 } 1167 } 1168 1169 /** 1170 * Enumerates the possible permissions that a user can have on a folder. 1171 */ 1172 public enum Permission { 1173 /** 1174 * The user can download the folder. 1175 */ 1176 CAN_DOWNLOAD("can_download"), 1177 1178 /** 1179 * The user can upload to the folder. 1180 */ 1181 CAN_UPLOAD("can_upload"), 1182 1183 /** 1184 * The user can rename the folder. 1185 */ 1186 CAN_RENAME("can_rename"), 1187 1188 /** 1189 * The user can delete the folder. 1190 */ 1191 CAN_DELETE("can_delete"), 1192 1193 /** 1194 * The user can share the folder. 1195 */ 1196 CAN_SHARE("can_share"), 1197 1198 /** 1199 * The user can invite collaborators to the folder. 1200 */ 1201 CAN_INVITE_COLLABORATOR("can_invite_collaborator"), 1202 1203 /** 1204 * The user can set the access level for shared links to the folder. 1205 */ 1206 CAN_SET_SHARE_ACCESS("can_set_share_access"); 1207 1208 private final String jsonValue; 1209 1210 private Permission(String jsonValue) { 1211 this.jsonValue = jsonValue; 1212 } 1213 1214 static Permission fromJSONValue(String jsonValue) { 1215 return Permission.valueOf(jsonValue.toUpperCase()); 1216 } 1217 1218 String toJSONValue() { 1219 return this.jsonValue; 1220 } 1221 } 1222}