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