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