001package com.box.sdk;
002
003import com.eclipsesource.json.JsonArray;
004import com.eclipsesource.json.JsonObject;
005import com.eclipsesource.json.JsonValue;
006import java.io.InputStream;
007import java.net.URL;
008import java.util.ArrayList;
009import java.util.Collection;
010import java.util.HashMap;
011import java.util.Iterator;
012import java.util.List;
013import java.util.Map;
014
015/**
016 * Represents a Box user account.
017 *
018 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
019 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
020 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
021 */
022@BoxResourceType("user")
023public class BoxUser extends BoxCollaborator {
024
025    /**
026     * An array of all possible file fields that can be requested when calling {@link #getInfo(String...)}.
027     */
028    public static final String[] ALL_FIELDS = {"type", "id", "name", "login", "created_at", "modified_at", "role",
029        "language", "timezone", "space_amount", "space_used", "max_upload_size", "tracking_codes",
030        "can_see_managed_users", "is_sync_enabled", "is_external_collab_restricted", "status", "job_title", "phone",
031        "address", "avatar_url", "is_exempt_from_device_limits", "is_exempt_from_login_verification", "enterprise",
032        "my_tags", "hostname", "is_platform_access_only", "external_app_user_id"};
033
034    /**
035     * User URL Template.
036     */
037    public static final URLTemplate USER_URL_TEMPLATE = new URLTemplate("users/%s");
038    /**
039     * Get Me URL Template.
040     */
041    public static final URLTemplate GET_ME_URL = new URLTemplate("users/me");
042    /**
043     * Users URL Template.
044     */
045    public static final URLTemplate USERS_URL_TEMPLATE = new URLTemplate("users");
046    /**
047     * User Memberships URL Template.
048     */
049    public static final URLTemplate USER_MEMBERSHIPS_URL_TEMPLATE = new URLTemplate("users/%s/memberships");
050    /**
051     * E-Mail Alias URL Template.
052     */
053    public static final URLTemplate EMAIL_ALIAS_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases/%s");
054    /**
055     * E-Mail Aliases URL Template.
056     */
057    public static final URLTemplate EMAIL_ALIASES_URL_TEMPLATE = new URLTemplate("users/%s/email_aliases");
058    /**
059     * Move Folder To User Template.
060     */
061    public static final URLTemplate MOVE_FOLDER_TO_USER_TEMPLATE = new URLTemplate("users/%s/folders/%s");
062    /**
063     * User Avatar Template.
064     */
065    public static final URLTemplate USER_AVATAR_TEMPLATE = new URLTemplate("users/%s/avatar");
066
067    /**
068     * Constructs a BoxUser for a user with a given ID.
069     *
070     * @param api the API connection to be used by the user.
071     * @param id  the ID of the user.
072     */
073    public BoxUser(BoxAPIConnection api, String id) {
074        super(api, id);
075    }
076
077    /**
078     * Provisions a new app user in an enterprise using Box Developer Edition.
079     *
080     * @param api  the API connection to be used by the created user.
081     * @param name the name of the user.
082     * @return the created user's info.
083     */
084    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name) {
085        return createAppUser(api, name, new CreateUserParams());
086    }
087
088    /**
089     * Provisions a new app user in an enterprise with additional user information using Box Developer Edition.
090     *
091     * @param api    the API connection to be used by the created user.
092     * @param name   the name of the user.
093     * @param params additional user information.
094     * @return the created user's info.
095     */
096    public static BoxUser.Info createAppUser(BoxAPIConnection api, String name,
097                                             CreateUserParams params) {
098
099        params.setIsPlatformAccessOnly(true);
100        return createEnterpriseUser(api, null, name, params);
101    }
102
103    /**
104     * Provisions a new user in an enterprise.
105     *
106     * @param api   the API connection to be used by the created user.
107     * @param login the email address the user will use to login.
108     * @param name  the name of the user.
109     * @return the created user's info.
110     */
111    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name) {
112        return createEnterpriseUser(api, login, name, null);
113    }
114
115    /**
116     * Provisions a new user in an enterprise with additional user information.
117     *
118     * @param api    the API connection to be used by the created user.
119     * @param login  the email address the user will use to login.
120     * @param name   the name of the user.
121     * @param params additional user information.
122     * @return the created user's info.
123     */
124    public static BoxUser.Info createEnterpriseUser(BoxAPIConnection api, String login, String name,
125                                                    CreateUserParams params) {
126
127        JsonObject requestJSON = new JsonObject();
128        requestJSON.add("login", login);
129        requestJSON.add("name", name);
130
131        if (params != null) {
132            if (params.getRole() != null) {
133                requestJSON.add("role", params.getRole().toJSONValue());
134            }
135
136            if (params.getStatus() != null) {
137                requestJSON.add("status", params.getStatus().toJSONValue());
138            }
139
140            requestJSON.add("language", params.getLanguage());
141            requestJSON.add("is_sync_enabled", params.getIsSyncEnabled());
142            requestJSON.add("job_title", params.getJobTitle());
143            requestJSON.add("phone", params.getPhone());
144            requestJSON.add("address", params.getAddress());
145            requestJSON.add("space_amount", params.getSpaceAmount());
146            requestJSON.add("can_see_managed_users", params.getCanSeeManagedUsers());
147            requestJSON.add("timezone", params.getTimezone());
148            requestJSON.add("is_exempt_from_device_limits", params.getIsExemptFromDeviceLimits());
149            requestJSON.add("is_exempt_from_login_verification", params.getIsExemptFromLoginVerification());
150            requestJSON.add("is_platform_access_only", params.getIsPlatformAccessOnly());
151            requestJSON.add("external_app_user_id", params.getExternalAppUserId());
152            requestJSON.add("is_external_collab_restricted", params.getIsExternalCollabRestricted());
153            if (params.getTrackingCodes() != null) {
154                requestJSON.add("tracking_codes", toTrackingCodesJson(params.getTrackingCodes()));
155            }
156        }
157
158        URL url = USERS_URL_TEMPLATE.build(api.getBaseURL());
159        BoxJSONRequest request = new BoxJSONRequest(api, url, "POST");
160        request.setBody(requestJSON.toString());
161        BoxJSONResponse response = (BoxJSONResponse) request.send();
162        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
163
164        BoxUser createdUser = new BoxUser(api, responseJSON.get("id").asString());
165        return createdUser.new Info(responseJSON);
166    }
167
168    /**
169     * Gets the current user.
170     *
171     * @param api the API connection of the current user.
172     * @return the current user.
173     */
174    public static BoxUser getCurrentUser(BoxAPIConnection api) {
175        URL url = GET_ME_URL.build(api.getBaseURL());
176        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
177        BoxJSONResponse response = (BoxJSONResponse) request.send();
178        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
179        return new BoxUser(api, jsonObject.get("id").asString());
180    }
181
182    /**
183     * Returns an iterable containing all the enterprise users.
184     *
185     * @param api the API connection to be used when retrieving the users.
186     * @return an iterable containing all the enterprise users.
187     */
188    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api) {
189        return getAllEnterpriseUsers(api, false, null);
190    }
191
192
193    /**
194     * Returns an iterable containing all the enterprise users. Uses marker based pagination.
195     *
196     * @param api       the API connection to be used when retrieving the users.
197     * @param usemarker Boolean that determines whether to use marker based pagination.
198     * @param marker    The marker at which the iterator will begin.
199     * @return an iterable containing all the enterprise users.
200     */
201    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final boolean usemarker,
202                                                               final String marker) {
203        return getUsersInfoForType(api, null, null, null, usemarker, marker);
204    }
205
206    /**
207     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
208     * to retrieve from the API.
209     *
210     * @param api        the API connection to be used when retrieving the users.
211     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
212     *                   login. Can be null to not filter the results.
213     * @param fields     the fields to retrieve. Leave this out for the standard fields.
214     * @return an iterable containing all the enterprise users that matches the filter.
215     */
216    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(final BoxAPIConnection api, final String filterTerm,
217                                                               final String... fields) {
218        return getUsersInfoForType(api, filterTerm, null, null, false, null, fields);
219    }
220
221    /**
222     * Returns an iterable containing all the enterprise users that matches the filter and specifies which child fields
223     * to retrieve from the API. Uses marker based pagination.
224     *
225     * @param api        the API connection to be used when retrieving the users.
226     * @param filterTerm used to filter the results to only users starting with this string in either the name or the
227     *                   login. Can be null to not filter the results.
228     * @param usemarker  Boolean that determines whether to use marker based pagination.
229     * @param marker     The marker at which the iterator will begin.
230     * @param fields     the fields to retrieve. Leave this out for the standard fields.
231     * @return an iterable containing all the enterprise users that matches the filter.
232     */
233    public static Iterable<BoxUser.Info> getAllEnterpriseUsers(
234        final BoxAPIConnection api,
235        final String filterTerm,
236        final boolean usemarker,
237        final String marker,
238        final String... fields
239    ) {
240        return getUsersInfoForType(api, filterTerm, null, null, usemarker, marker, fields);
241    }
242
243    /**
244     * Gets a limited set of information about an external user. (A user collaborating
245     * on content owned by the enterprise). Note: Only fields the user has permission to
246     * see will be returned with values. Other fields will return a value of null.
247     *
248     * @param api        the API connection to be used when retrieving the users.
249     * @param filterTerm used to filter the results to only users matching the given login.
250     *                   This does exact match only, so if no filter term is passed in, nothing
251     *                   will be returned.
252     * @param fields     the fields to retrieve. Leave this out for the standard fields.
253     * @return an iterable containing external users matching the given email
254     */
255    public static Iterable<BoxUser.Info> getExternalUsers(final BoxAPIConnection api, final String filterTerm,
256                                                          final String... fields) {
257        return getUsersInfoForType(api, filterTerm, "external", null, false, null, fields);
258    }
259
260    /**
261     * Gets a limited set of information about an external user. (A user collaborating
262     * on content owned by the enterprise). Note: Only fields the user has permission to
263     * see will be returned with values. Other fields will return a value of null. Uses marker based pagination.
264     *
265     * @param api        the API connection to be used when retrieving the users.
266     * @param filterTerm used to filter the results to only users matching the given login.
267     *                   This does exact match only, so if no filter term is passed in, nothing
268     *                   will be returned.
269     * @param usemarker  Boolean that determines whether to use marker based pagination.
270     * @param marker     The marker at which the iterator will begin.
271     * @param fields     the fields to retrieve. Leave this out for the standard fields.
272     * @return an iterable containing external users matching the given email
273     */
274    public static Iterable<BoxUser.Info> getExternalUsers(
275        final BoxAPIConnection api,
276        final String filterTerm,
277        final boolean usemarker,
278        final String marker,
279        final String... fields
280    ) {
281        return getUsersInfoForType(api, filterTerm, "external", null, usemarker, marker, fields);
282    }
283
284    /**
285     * Gets any managed users that match the filter term as well as any external users that
286     * match the filter term. For managed users it matches any users names or emails that
287     * start with the term. For external, it only does full match on email. This method
288     * is ideal to use in the case where you have a full email for a user and you don't
289     * know if they're managed or external.
290     *
291     * @param api        the API connection to be used when retrieving the users.
292     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
293     * @param fields     the fields to retrieve. Leave this out for the standard fields.
294     * @return an iterable containing users matching the given email
295     */
296    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
297        final BoxAPIConnection api,
298        final String filterTerm,
299        final String... fields
300    ) {
301        return getUsersInfoForType(api, filterTerm, "all", null, false, null, fields);
302    }
303
304    /**
305     * Gets any managed users that match the filter term as well as any external users that
306     * match the filter term. For managed users it matches any users names or emails that
307     * start with the term. For external, it only does full match on email. This method
308     * is ideal to use in the case where you have a full email for a user and you don't
309     * know if they're managed or external. Uses marker based pagination.
310     *
311     * @param api        the API connection to be used when retrieving the users.
312     * @param filterTerm The filter term to lookup users by (login for external, login or name for managed)
313     * @param usemarker  Boolean that determines whether to use marker based pagination.
314     * @param marker     The marker at which the iterator will begin.
315     * @param fields     the fields to retrieve. Leave this out for the standard fields.
316     * @return an iterable containing users matching the given email
317     */
318    public static Iterable<BoxUser.Info> getAllEnterpriseOrExternalUsers(
319        final BoxAPIConnection api,
320        final String filterTerm,
321        final boolean usemarker,
322        final String marker,
323        final String... fields
324    ) {
325        return getUsersInfoForType(api, filterTerm, "all", null, usemarker, marker, fields);
326    }
327
328    /**
329     * Gets any app users that has an exact match with the externalAppUserId term.
330     *
331     * @param api               the API connection to be used when retrieving the users.
332     * @param externalAppUserId the external app user id that has been set for app user
333     * @param fields            the fields to retrieve. Leave this out for the standard fields.
334     * @return an iterable containing users matching the given email
335     */
336    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
337        final BoxAPIConnection api,
338        final String externalAppUserId,
339        final String... fields
340    ) {
341        return getUsersInfoForType(api, null, null, externalAppUserId, false, null, fields);
342    }
343
344    /**
345     * Gets any app users that has an exact match with the externalAppUserId term using marker based pagination.
346     *
347     * @param api               the API connection to be used when retrieving the users.
348     * @param externalAppUserId the external app user id that has been set for app user
349     * @param usemarker         Boolean that determines whether to use marker based pagination.
350     * @param marker            The marker at which the iterator will begin.
351     * @param fields            the fields to retrieve. Leave this out for the standard fields.
352     * @return an iterable containing users matching the given email
353     */
354    public static Iterable<BoxUser.Info> getAppUsersByExternalAppUserID(
355        final BoxAPIConnection api,
356        final String externalAppUserId,
357        final boolean usemarker,
358        String marker,
359        final String... fields
360    ) {
361        return getUsersInfoForType(api, null, null, externalAppUserId, usemarker, marker, fields);
362    }
363
364    /**
365     * Helper method to abstract out the common logic from the various users methods.
366     *
367     * @param api               the API connection to be used when retrieving the users.
368     * @param filterTerm        The filter term to lookup users by (login for external, login or name for managed)
369     * @param userType          The type of users we want to search with this request.
370     *                          Valid values are 'managed' (enterprise users), 'external' or 'all'
371     * @param externalAppUserId the external app user id that has been set for an app user
372     * @param usemarker         Boolean that determines whether to use marker based pagination.
373     * @param marker            The marker at which the iterator will begin.
374     * @param fields            the fields to retrieve. Leave this out for the standard fields.
375     * @return An iterator over the selected users.
376     */
377    private static Iterable<BoxUser.Info> getUsersInfoForType(
378        final BoxAPIConnection api,
379        final String filterTerm,
380        final String userType,
381        final String externalAppUserId,
382        final boolean usemarker,
383        final String marker,
384        final String... fields
385    ) {
386
387        final QueryStringBuilder builder = new QueryStringBuilder();
388        if (filterTerm != null) {
389            builder.appendParam("filter_term", filterTerm);
390        }
391        if (userType != null) {
392            builder.appendParam("user_type", userType);
393        }
394        if (externalAppUserId != null) {
395            builder.appendParam("external_app_user_id", externalAppUserId);
396        }
397        if (usemarker) {
398            builder.appendParam("usemarker", "true");
399        }
400        if (fields.length > 0) {
401            builder.appendParam("fields", fields);
402        }
403        final URL url = USERS_URL_TEMPLATE.buildWithQuery(api.getBaseURL(), builder.toString());
404
405        if (usemarker) {
406            return new BoxResourceIterable<BoxUser.Info>(api, url, 100, null, marker) {
407                @Override
408                protected BoxUser.Info factory(JsonObject jsonObject) {
409                    BoxUser user = new BoxUser(api, jsonObject.get("id").asString());
410                    return user.new Info(jsonObject);
411                }
412            };
413        } else {
414            return new Iterable<BoxUser.Info>() {
415                public Iterator<BoxUser.Info> iterator() {
416                    return new BoxUserIterator(api, url);
417                }
418            };
419        }
420    }
421
422    private static JsonArray toTrackingCodesJson(Map<String, String> trackingCodes) {
423        JsonArray trackingCodesJsonArray = new JsonArray();
424        for (String attrKey : trackingCodes.keySet()) {
425            JsonObject trackingCode = new JsonObject();
426            trackingCode.set("type", "tracking_code");
427            trackingCode.set("name", attrKey);
428            trackingCode.set("value", trackingCodes.get(attrKey));
429            trackingCodesJsonArray.add(trackingCode);
430        }
431        return trackingCodesJsonArray;
432    }
433
434    /**
435     * Gets information about this user.
436     *
437     * @param fields the optional fields to retrieve.
438     * @return info about this user.
439     */
440    public BoxUser.Info getInfo(String... fields) {
441        URL url;
442        if (fields.length > 0) {
443            String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
444            url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
445        } else {
446            url = USER_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
447        }
448        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
449        BoxJSONResponse response = (BoxJSONResponse) request.send();
450        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
451        return new Info(jsonObject);
452    }
453
454    /**
455     * Gets information about all of the group memberships for this user.
456     * Does not support paging.
457     *
458     * <p>Note: This method is only available to enterprise admins.</p>
459     *
460     * @return a collection of information about the group memberships for this user.
461     */
462    public Collection<BoxGroupMembership.Info> getMemberships() {
463        BoxAPIConnection api = this.getAPI();
464        URL url = USER_MEMBERSHIPS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
465
466        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
467        BoxJSONResponse response = (BoxJSONResponse) request.send();
468        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
469
470        int entriesCount = responseJSON.get("total_count").asInt();
471        Collection<BoxGroupMembership.Info> memberships = new ArrayList<BoxGroupMembership.Info>(entriesCount);
472        JsonArray entries = responseJSON.get("entries").asArray();
473        for (JsonValue entry : entries) {
474            JsonObject entryObject = entry.asObject();
475            BoxGroupMembership membership = new BoxGroupMembership(api, entryObject.get("id").asString());
476            BoxGroupMembership.Info info = membership.new Info(entryObject);
477            memberships.add(info);
478        }
479
480        return memberships;
481    }
482
483    /**
484     * Gets information about all of the group memberships for this user as iterable with paging support.
485     *
486     * @param fields the fields to retrieve.
487     * @return an iterable with information about the group memberships for this user.
488     */
489    public Iterable<BoxGroupMembership.Info> getAllMemberships(String... fields) {
490        final QueryStringBuilder builder = new QueryStringBuilder();
491        if (fields.length > 0) {
492            builder.appendParam("fields", fields);
493        }
494        return new Iterable<BoxGroupMembership.Info>() {
495            public Iterator<BoxGroupMembership.Info> iterator() {
496                URL url = USER_MEMBERSHIPS_URL_TEMPLATE.buildWithQuery(
497                    BoxUser.this.getAPI().getBaseURL(), builder.toString(), BoxUser.this.getID());
498                return new BoxGroupMembershipIterator(BoxUser.this.getAPI(), url);
499            }
500        };
501    }
502
503    /**
504     * Adds a new email alias to this user's account.
505     *
506     * @param email the email address to add as an alias.
507     * @return the newly created email alias.
508     */
509    public EmailAlias addEmailAlias(String email) {
510        return this.addEmailAlias(email, false);
511    }
512
513    /**
514     * Adds a new email alias to this user's account and confirms it without user interaction.
515     * This functionality is only available for enterprise admins.
516     *
517     * @param email       the email address to add as an alias.
518     * @param isConfirmed whether or not the email alias should be automatically confirmed.
519     * @return the newly created email alias.
520     */
521    public EmailAlias addEmailAlias(String email, boolean isConfirmed) {
522        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
523        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
524
525        JsonObject requestJSON = new JsonObject()
526            .add("email", email);
527
528        if (isConfirmed) {
529            requestJSON.add("is_confirmed", isConfirmed);
530        }
531
532        request.setBody(requestJSON.toString());
533        BoxJSONResponse response = (BoxJSONResponse) request.send();
534        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
535        return new EmailAlias(responseJSON);
536    }
537
538    /**
539     * Deletes an email alias from this user's account.
540     *
541     * <p>The IDs of the user's email aliases can be found by calling {@link #getEmailAliases}.</p>
542     *
543     * @param emailAliasID the ID of the email alias to delete.
544     */
545    public void deleteEmailAlias(String emailAliasID) {
546        URL url = EMAIL_ALIAS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), emailAliasID);
547        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
548        BoxAPIResponse response = request.send();
549        response.disconnect();
550    }
551
552    /**
553     * Gets a collection of all the email aliases for this user.
554     *
555     * <p>Note that the user's primary login email is not included in the collection of email aliases.</p>
556     *
557     * @return a collection of all the email aliases for this user.
558     */
559    public Collection<EmailAlias> getEmailAliases() {
560        URL url = EMAIL_ALIASES_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
561        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
562        BoxJSONResponse response = (BoxJSONResponse) request.send();
563        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
564
565        int totalCount = responseJSON.get("total_count").asInt();
566        Collection<EmailAlias> emailAliases = new ArrayList<EmailAlias>(totalCount);
567        JsonArray entries = responseJSON.get("entries").asArray();
568        for (JsonValue value : entries) {
569            JsonObject emailAliasJSON = value.asObject();
570            emailAliases.add(new EmailAlias(emailAliasJSON));
571        }
572
573        return emailAliases;
574    }
575
576    /**
577     * Deletes a user from an enterprise account.
578     *
579     * @param notifyUser whether or not to send an email notification to the user that their account has been deleted.
580     * @param force      whether or not this user should be deleted even if they still own files.
581     */
582    public void delete(boolean notifyUser, boolean force) {
583        String queryString = new QueryStringBuilder()
584            .appendParam("notify", String.valueOf(notifyUser))
585            .appendParam("force", String.valueOf(force))
586            .toString();
587
588        URL url = USER_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID());
589        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
590        BoxAPIResponse response = request.send();
591        response.disconnect();
592    }
593
594    /**
595     * Updates the information about this user with any info fields that have been modified locally.
596     *
597     * <p>Note: This method is only available to enterprise admins.</p>
598     *
599     * @param info info the updated info.
600     */
601    public void updateInfo(BoxUser.Info info) {
602        URL url = USER_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
603        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
604        request.setBody(info.getPendingChanges());
605        BoxJSONResponse response = (BoxJSONResponse) request.send();
606        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
607        info.update(jsonObject);
608    }
609
610    /**
611     * @param sourceUserID the user id of the user whose files will be the source for this operation
612     * @return info for the newly created folder
613     * @deprecated As of release 2.22.0, replaced by {@link #transferContent(String)} ()}
614     * <p>
615     * <p>
616     * Moves all of the owned content from within one user’s folder into a new folder in another user's account.
617     * You can move folders across users as long as the you have administrative permissions and the 'source'
618     * user owns the folders. Per the documentation at the link below, this will move everything from the root
619     * folder, as this is currently the only mode of operation supported.
620     * <p>
621     * See also https://developer.box.com/en/reference/put-users-id-folders-id/
622     */
623    @Deprecated
624    public BoxFolder.Info moveFolderToUser(String sourceUserID) {
625        // Currently the API only supports moving of the root folder (0), hence the hard coded "0"
626        URL url = MOVE_FOLDER_TO_USER_TEMPLATE.build(this.getAPI().getBaseURL(), sourceUserID, "0");
627        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
628        JsonObject idValue = new JsonObject();
629        idValue.add("id", this.getID());
630        JsonObject ownedBy = new JsonObject();
631        ownedBy.add("owned_by", idValue);
632        request.setBody(ownedBy.toString());
633        BoxJSONResponse response = (BoxJSONResponse) request.send();
634        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
635        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
636
637        return movedFolder.new Info(responseJSON);
638    }
639
640    /**
641     * Moves all of the owned content from within one user’s folder into a new folder in another user's account.
642     * You can move folders across users as long as the you have administrative permissions and the 'source'
643     * user owns the folders. Per the documentation at the link below, this will move everything from the root
644     * folder, as this is currently the only mode of operation supported.
645     * <p>
646     * See also https://developer.box.com/en/reference/put-users-id-folders-id/
647     *
648     * @param destinationUserID the user id of the user that you wish to transfer content to.
649     * @return info for the newly created folder.
650     */
651    public BoxFolder.Info transferContent(String destinationUserID) {
652        URL url = MOVE_FOLDER_TO_USER_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), "0");
653        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
654        JsonObject destinationUser = new JsonObject();
655        destinationUser.add("id", destinationUserID);
656        JsonObject ownedBy = new JsonObject();
657        ownedBy.add("owned_by", destinationUser);
658        request.setBody(ownedBy.toString());
659        BoxJSONResponse response = (BoxJSONResponse) request.send();
660        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
661        BoxFolder movedFolder = new BoxFolder(this.getAPI(), responseJSON.get("id").asString());
662
663        return movedFolder.new Info(responseJSON);
664    }
665
666    /**
667     * Retrieves the avatar of a user as an InputStream.
668     *
669     * @return InputStream representing the user avater.
670     */
671    public InputStream getAvatar() {
672        URL url = USER_AVATAR_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
673        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET");
674        BoxAPIResponse response = request.send();
675
676        return response.getBody();
677    }
678
679    /**
680     * Enumerates the possible roles that a user can have within an enterprise.
681     */
682    public enum Role {
683        /**
684         * The user is an administrator of their enterprise.
685         */
686        ADMIN("admin"),
687
688        /**
689         * The user is a co-administrator of their enterprise.
690         */
691        COADMIN("coadmin"),
692
693        /**
694         * The user is a regular user within their enterprise.
695         */
696        USER("user");
697
698        private final String jsonValue;
699
700        Role(String jsonValue) {
701            this.jsonValue = jsonValue;
702        }
703
704        static Role fromJSONValue(String jsonValue) {
705            return Role.valueOf(jsonValue.toUpperCase());
706        }
707
708        String toJSONValue() {
709            return this.jsonValue;
710        }
711    }
712
713    /**
714     * Enumerates the possible statuses that a user's account can have.
715     */
716    public enum Status {
717        /**
718         * The user's account is active.
719         */
720        ACTIVE("active"),
721
722        /**
723         * The user's account is inactive.
724         */
725        INACTIVE("inactive"),
726
727        /**
728         * The user's account cannot delete or edit content.
729         */
730        CANNOT_DELETE_EDIT("cannot_delete_edit"),
731
732        /**
733         * The user's account cannot delete, edit, or upload content.
734         */
735        CANNOT_DELETE_EDIT_UPLOAD("cannot_delete_edit_upload");
736
737        private final String jsonValue;
738
739        Status(String jsonValue) {
740            this.jsonValue = jsonValue;
741        }
742
743        static Status fromJSONValue(String jsonValue) {
744            return Status.valueOf(jsonValue.toUpperCase());
745        }
746
747        String toJSONValue() {
748            return this.jsonValue;
749        }
750    }
751
752    /**
753     * Contains information about a BoxUser.
754     */
755    public class Info extends BoxCollaborator.Info {
756        private String login;
757        private Role role;
758        private String language;
759        private String timezone;
760        private long spaceAmount;
761        private long spaceUsed;
762        private long maxUploadSize;
763        private boolean canSeeManagedUsers;
764        private boolean isSyncEnabled;
765        private boolean isExternalCollabRestricted;
766        private Status status;
767        private String jobTitle;
768        private String phone;
769        private String address;
770        private String avatarURL;
771        private boolean isExemptFromDeviceLimits;
772        private boolean isExemptFromLoginVerification;
773        private boolean isPasswordResetRequired;
774        private boolean isPlatformAccessOnly;
775        private String externalAppUserId;
776        private BoxEnterprise enterprise;
777        private List<String> myTags;
778        private String hostname;
779        private Map<String, String> trackingCodes;
780
781        /**
782         * Constructs an empty Info object.
783         */
784        public Info() {
785            super();
786        }
787
788        /**
789         * Constructs an Info object by parsing information from a JSON string.
790         *
791         * @param json the JSON string to parse.
792         */
793        public Info(String json) {
794            super(json);
795        }
796
797        Info(JsonObject jsonObject) {
798            super(jsonObject);
799        }
800
801        @Override
802        public BoxUser getResource() {
803            return BoxUser.this;
804        }
805
806        /**
807         * Gets the email address the user uses to login.
808         *
809         * @return the email address the user uses to login.
810         */
811        public String getLogin() {
812            return this.login;
813        }
814
815        /**
816         * Sets the email address the user uses to login. The new login must be one of the user's already confirmed
817         * email aliases.
818         *
819         * @param login one of the user's confirmed email aliases.
820         */
821        public void setLogin(String login) {
822            this.login = login;
823            this.addPendingChange("login", login);
824        }
825
826        /**
827         * Gets the user's enterprise role.
828         *
829         * @return the user's enterprise role.
830         */
831        public Role getRole() {
832            return this.role;
833        }
834
835        /**
836         * Sets the user's role in their enterprise.
837         *
838         * @param role the user's new role in their enterprise.
839         */
840        public void setRole(Role role) {
841            this.role = role;
842            this.addPendingChange("role", role.name().toLowerCase());
843        }
844
845        /**
846         * Gets the language of the user.
847         *
848         * @return the language of the user.
849         */
850        public String getLanguage() {
851            return this.language;
852        }
853
854        /**
855         * Sets the language of the user.
856         *
857         * @param language the new language of the user.
858         */
859        public void setLanguage(String language) {
860            this.language = language;
861            this.addPendingChange("language", language);
862        }
863
864        /**
865         * Gets the timezone of the user.
866         *
867         * @return the timezone of the user.
868         */
869        public String getTimezone() {
870            return this.timezone;
871        }
872
873        /**
874         * Sets the timezone of the user.
875         *
876         * @param timezone the new timezone of the user.
877         */
878        public void setTimezone(String timezone) {
879            this.timezone = timezone;
880            this.addPendingChange("timezone", timezone);
881        }
882
883        /**
884         * Gets the user's total available space in bytes.
885         *
886         * @return the user's total available space in bytes.
887         */
888        public long getSpaceAmount() {
889            return this.spaceAmount;
890        }
891
892        /**
893         * Sets the user's total available space in bytes.
894         *
895         * @param spaceAmount the new amount of space available to the user in bytes, or -1 for unlimited storage.
896         */
897        public void setSpaceAmount(long spaceAmount) {
898            this.spaceAmount = spaceAmount;
899            this.addPendingChange("space_amount", spaceAmount);
900        }
901
902        /**
903         * Gets the amount of space the user has used in bytes.
904         *
905         * @return the amount of space the user has used in bytes.
906         */
907        public long getSpaceUsed() {
908            return this.spaceUsed;
909        }
910
911        /**
912         * Gets the maximum individual file size in bytes the user can have.
913         *
914         * @return the maximum individual file size in bytes the user can have.
915         */
916        public long getMaxUploadSize() {
917            return this.maxUploadSize;
918        }
919
920        /**
921         * Gets the user's current account status.
922         *
923         * @return the user's current account status.
924         */
925        public Status getStatus() {
926            return this.status;
927        }
928
929        /**
930         * Sets the user's current account status.
931         *
932         * @param status the user's new account status.
933         */
934        public void setStatus(Status status) {
935            this.status = status;
936            this.addPendingChange("status", status.name().toLowerCase());
937        }
938
939        /**
940         * Gets the job title of the user.
941         *
942         * @return the job title of the user.
943         */
944        public String getJobTitle() {
945            return this.jobTitle;
946        }
947
948        /**
949         * Sets the job title of the user.
950         *
951         * @param jobTitle the new job title of the user.
952         */
953        public void setJobTitle(String jobTitle) {
954            this.jobTitle = jobTitle;
955            this.addPendingChange("job_title", jobTitle);
956        }
957
958        /**
959         * Gets the phone number of the user.
960         *
961         * @return the phone number of the user.
962         */
963        public String getPhone() {
964            return this.phone;
965        }
966
967        /**
968         * Sets the phone number of the user.
969         *
970         * @param phone the new phone number of the user.
971         */
972        public void setPhone(String phone) {
973            this.phone = phone;
974            this.addPendingChange("phone", phone);
975        }
976
977        /**
978         * Gets the address of the user.
979         *
980         * @return the address of the user.
981         */
982        public String getAddress() {
983            return this.address;
984        }
985
986        /**
987         * Sets the address of the user.
988         *
989         * @param address the new address of the user.
990         */
991        public void setAddress(String address) {
992            this.address = address;
993            this.addPendingChange("address", address);
994        }
995
996        /**
997         * Gets the URL of the user's avatar.
998         *
999         * @return the URL of the user's avatar.
1000         */
1001        public String getAvatarURL() {
1002            return this.avatarURL;
1003        }
1004
1005        /**
1006         * Gets the enterprise that the user belongs to.
1007         *
1008         * @return the enterprise that the user belongs to.
1009         */
1010        public BoxEnterprise getEnterprise() {
1011            return this.enterprise;
1012        }
1013
1014        /**
1015         * Removes the user from their enterprise and converts them to a standalone free user.
1016         */
1017        public void removeEnterprise() {
1018            this.removeChildObject("enterprise");
1019            this.enterprise = null;
1020            this.addChildObject("enterprise", null);
1021        }
1022
1023        /**
1024         * Gets whether or not the user can use Box Sync.
1025         *
1026         * @return true if the user can use Box Sync; otherwise false.
1027         */
1028        public boolean getIsSyncEnabled() {
1029            return this.isSyncEnabled;
1030        }
1031
1032        /**
1033         * Sets whether or not the user can use Box Sync.
1034         *
1035         * @param enabled whether or not the user can use Box Sync.
1036         */
1037        public void setIsSyncEnabled(boolean enabled) {
1038            this.isSyncEnabled = enabled;
1039            this.addPendingChange("is_sync_enabled", enabled);
1040        }
1041
1042        /**
1043         * Gets whether this user is allowed or not to collaborate with users outside their enterprise.
1044         *
1045         * @return true if this user is not allowed to collaborate with users outside their enterprise; otherwise false.
1046         */
1047        public boolean getIsExternalCollabRestricted() {
1048            return this.isExternalCollabRestricted;
1049        }
1050
1051        /**
1052         * Sets whether this user is allowed or not to collaborate with users outside their enterprise.
1053         *
1054         * @param isExternalCollabRestricted whether the user is allowed to collaborate outside their enterprise.
1055         */
1056        public void setIsExternalCollabRestricted(boolean isExternalCollabRestricted) {
1057            this.isExternalCollabRestricted = isExternalCollabRestricted;
1058            this.addPendingChange("is_external_collab_restricted", isExternalCollabRestricted);
1059        }
1060
1061        /**
1062         * Gets whether or not the user can see other enterprise users in their contact list.
1063         *
1064         * @return true if the user can see other enterprise users in their contact list; otherwise false.
1065         */
1066        public boolean getCanSeeManagedUsers() {
1067            return this.canSeeManagedUsers;
1068        }
1069
1070        /**
1071         * Sets whether or not the user can see other enterprise users in their contact list.
1072         *
1073         * @param canSeeManagedUsers whether or not the user can see other enterprise users in their contact list.
1074         */
1075        public void setCanSeeManagedUsers(boolean canSeeManagedUsers) {
1076            this.canSeeManagedUsers = canSeeManagedUsers;
1077            this.addPendingChange("can_see_managed_users", canSeeManagedUsers);
1078        }
1079
1080        /**
1081         * Gets whether or not the user is exempt from enterprise device limits.
1082         *
1083         * @return true if the user is exempt from enterprise device limits; otherwise false.
1084         */
1085        public boolean getIsExemptFromDeviceLimits() {
1086            return this.isExemptFromDeviceLimits;
1087        }
1088
1089        /**
1090         * Sets whether or not the user is exempt from enterprise device limits.
1091         *
1092         * @param isExemptFromDeviceLimits whether or not the user is exempt from enterprise device limits.
1093         */
1094        public void setIsExemptFromDeviceLimits(boolean isExemptFromDeviceLimits) {
1095            this.isExemptFromDeviceLimits = isExemptFromDeviceLimits;
1096            this.addPendingChange("is_exempt_from_device_limits", isExemptFromDeviceLimits);
1097        }
1098
1099        /**
1100         * Gets whether or not the user must use two-factor authentication.
1101         *
1102         * @return true if the user must use two-factor authentication; otherwise false.
1103         */
1104        public boolean getIsExemptFromLoginVerification() {
1105            return this.isExemptFromLoginVerification;
1106        }
1107
1108        /**
1109         * Sets whether or not the user must use two-factor authentication.
1110         *
1111         * @param isExemptFromLoginVerification whether or not the user must use two-factor authentication.
1112         */
1113        public void setIsExemptFromLoginVerification(boolean isExemptFromLoginVerification) {
1114            this.isExemptFromLoginVerification = isExemptFromLoginVerification;
1115            this.addPendingChange("is_exempt_from_login_verification", isExemptFromLoginVerification);
1116        }
1117
1118        /**
1119         * Gets whether or not the user is required to reset password.
1120         *
1121         * @return true if the user is required to reset password; otherwise false.
1122         */
1123        public boolean getIsPasswordResetRequired() {
1124            return this.isPasswordResetRequired;
1125        }
1126
1127        /**
1128         * Sets whether or not the user is required to reset password.
1129         *
1130         * @param isPasswordResetRequired whether or not the user is required to reset password.
1131         */
1132        public void setIsPasswordResetRequired(boolean isPasswordResetRequired) {
1133            this.isPasswordResetRequired = isPasswordResetRequired;
1134            this.addPendingChange("is_password_reset_required", isPasswordResetRequired);
1135        }
1136
1137        /**
1138         * Gets whether or not the user we are creating is an app user with Box Developer Edition.
1139         *
1140         * @return true if the new user is an app user for Box Developer Addition; otherwise false.
1141         */
1142        public boolean getIsPlatformAccessOnly() {
1143            return this.isPlatformAccessOnly;
1144        }
1145
1146        /**
1147         * Gets the external app user id that has been set for the app user.
1148         *
1149         * @return the external app user id.
1150         */
1151        public String getExternalAppUserId() {
1152            return this.externalAppUserId;
1153        }
1154
1155        /**
1156         * Sets the external app user id.
1157         *
1158         * @param externalAppUserId external app user id.
1159         */
1160        public void setExternalAppUserId(String externalAppUserId) {
1161            this.externalAppUserId = externalAppUserId;
1162            this.addPendingChange("external_app_user_id", externalAppUserId);
1163        }
1164
1165        /**
1166         * Gets the tags for all files and folders owned by this user.
1167         *
1168         * @return the tags for all files and folders owned by this user.
1169         */
1170        public List<String> getMyTags() {
1171            return this.myTags;
1172        }
1173
1174        /**
1175         * Gets the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1176         *
1177         * @return the root (protocol, subdomain, domain) of any links that need to be generated for this user.
1178         */
1179        public String getHostname() {
1180            return this.hostname;
1181        }
1182
1183        /**
1184         * Gets the tracking defined for each entity.
1185         *
1186         * @return a Map with tracking codes.
1187         */
1188        public Map<String, String> getTrackingCodes() {
1189            return this.trackingCodes;
1190        }
1191
1192        /**
1193         * Allows admin to set attributes specific for a group of users.
1194         *
1195         * @param trackingCodes a Map representing the user's new tracking codes
1196         */
1197        public void setTrackingCodes(Map<String, String> trackingCodes) {
1198            this.trackingCodes = trackingCodes;
1199            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1200        }
1201
1202        /**
1203         * Allows the admin to append new tracking codes to the previous existing list.
1204         *
1205         * @param name  the name or `key` of the attribute to set.
1206         * @param value the value of the attribute to set.
1207         */
1208        public void appendTrackingCodes(String name, String value) {
1209            this.getTrackingCodes().put(name, value);
1210            this.addPendingChange("tracking_codes", toTrackingCodesJson(this.trackingCodes));
1211        }
1212
1213        @Override
1214        protected void parseJSONMember(JsonObject.Member member) {
1215            super.parseJSONMember(member);
1216
1217            JsonValue value = member.getValue();
1218            String memberName = member.getName();
1219            try {
1220                if (memberName.equals("login")) {
1221                    this.login = value.asString();
1222                } else if (memberName.equals("role")) {
1223                    this.role = Role.fromJSONValue(value.asString());
1224                } else if (memberName.equals("language")) {
1225                    this.language = value.asString();
1226                } else if (memberName.equals("timezone")) {
1227                    this.timezone = value.asString();
1228                } else if (memberName.equals("space_amount")) {
1229                    this.spaceAmount = Double.valueOf(value.toString()).longValue();
1230                } else if (memberName.equals("space_used")) {
1231                    this.spaceUsed = Double.valueOf(value.toString()).longValue();
1232                } else if (memberName.equals("max_upload_size")) {
1233                    this.maxUploadSize = Double.valueOf(value.toString()).longValue();
1234                } else if (memberName.equals("status")) {
1235                    this.status = Status.fromJSONValue(value.asString());
1236                } else if (memberName.equals("job_title")) {
1237                    this.jobTitle = value.asString();
1238                } else if (memberName.equals("phone")) {
1239                    this.phone = value.asString();
1240                } else if (memberName.equals("address")) {
1241                    this.address = value.asString();
1242                } else if (memberName.equals("avatar_url")) {
1243                    this.avatarURL = value.asString();
1244                } else if (memberName.equals("can_see_managed_users")) {
1245                    this.canSeeManagedUsers = value.asBoolean();
1246                } else if (memberName.equals("is_sync_enabled")) {
1247                    this.isSyncEnabled = value.asBoolean();
1248                } else if (memberName.equals("is_external_collab_restricted")) {
1249                    this.isExternalCollabRestricted = value.asBoolean();
1250                } else if (memberName.equals("is_exempt_from_device_limits")) {
1251                    this.isExemptFromDeviceLimits = value.asBoolean();
1252                } else if (memberName.equals("is_exempt_from_login_verification")) {
1253                    this.isExemptFromLoginVerification = value.asBoolean();
1254                } else if (memberName.equals("is_password_reset_required")) {
1255                    this.isPasswordResetRequired = value.asBoolean();
1256                } else if (memberName.equals("is_platform_access_only")) {
1257                    this.isPlatformAccessOnly = value.asBoolean();
1258                } else if (memberName.equals("external_app_user_id")) {
1259                    this.externalAppUserId = value.asString();
1260                } else if (memberName.equals("enterprise")) {
1261                    JsonObject jsonObject = value.asObject();
1262                    if (this.enterprise == null) {
1263                        this.enterprise = new BoxEnterprise(jsonObject);
1264                    } else {
1265                        this.enterprise.update(jsonObject);
1266                    }
1267                } else if (memberName.equals("my_tags")) {
1268                    this.myTags = this.parseMyTags(value.asArray());
1269                } else if (memberName.equals("hostname")) {
1270                    this.hostname = value.asString();
1271                } else if (memberName.equals("tracking_codes")) {
1272                    this.trackingCodes = this.parseTrackingCodes(value.asArray());
1273                }
1274            } catch (Exception e) {
1275                throw new BoxDeserializationException(memberName, value.toString(), e);
1276            }
1277
1278        }
1279
1280        private List<String> parseMyTags(JsonArray jsonArray) {
1281            List<String> myTags = new ArrayList<String>(jsonArray.size());
1282            for (JsonValue value : jsonArray) {
1283                myTags.add(value.asString());
1284            }
1285
1286            return myTags;
1287        }
1288
1289        private Map<String, String> parseTrackingCodes(JsonArray jsonArray) {
1290            Map<String, String> result = new HashMap<String, String>();
1291            if (jsonArray == null) {
1292                return null;
1293            }
1294            List<JsonValue> valuesList = jsonArray.values();
1295            for (JsonValue jsonValue : valuesList) {
1296                JsonObject object = jsonValue.asObject();
1297                result.put(object.get("name").asString().toString(), object.get("value").asString().toString());
1298            }
1299            return result;
1300        }
1301    }
1302}