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