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