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