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