001package com.box.sdk;
002
003import java.net.URL;
004import java.text.ParseException;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Date;
008
009import com.eclipsesource.json.JsonArray;
010import com.eclipsesource.json.JsonObject;
011import com.eclipsesource.json.JsonValue;
012
013/**
014 * Represents a collaboration between a user and another user or group. Collaborations are Box's equivalent of access
015 * control lists. They can be used to set and apply permissions for users or groups to folders.
016 *
017 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked
018 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error
019 * handling for errors related to the Box REST API, you should capture this exception explicitly.</p>
020 */
021@BoxResourceType("collaboration")
022public class BoxCollaboration extends BoxResource {
023    private static final URLTemplate COLLABORATIONS_URL_TEMPLATE = new URLTemplate("collaborations");
024    private static final URLTemplate PENDING_COLLABORATIONS_URL = new URLTemplate("collaborations?status=pending");
025    private static final URLTemplate COLLABORATION_URL_TEMPLATE = new URLTemplate("collaborations/%s");
026
027    /**
028     * Constructs a BoxCollaboration for a collaboration with a given ID.
029     * @param  api the API connection to be used by the collaboration.
030     * @param  id  the ID of the collaboration.
031     */
032    public BoxCollaboration(BoxAPIConnection api, String id) {
033        super(api, id);
034    }
035
036    /**
037     * Gets all pending collaboration invites for the current user.
038     * @param  api the API connection to use.
039     * @return     a collection of pending collaboration infos.
040     */
041    public static Collection<Info> getPendingCollaborations(BoxAPIConnection api) {
042        URL url = PENDING_COLLABORATIONS_URL.build(api.getBaseURL());
043
044        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
045        BoxJSONResponse response = (BoxJSONResponse) request.send();
046        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
047
048        int entriesCount = responseJSON.get("total_count").asInt();
049        Collection<BoxCollaboration.Info> collaborations = new ArrayList<BoxCollaboration.Info>(entriesCount);
050        JsonArray entries = responseJSON.get("entries").asArray();
051        for (JsonValue entry : entries) {
052            JsonObject entryObject = entry.asObject();
053            BoxCollaboration collaboration = new BoxCollaboration(api, entryObject.get("id").asString());
054            BoxCollaboration.Info info = collaboration.new Info(entryObject);
055            collaborations.add(info);
056        }
057
058        return collaborations;
059    }
060
061    /**
062     * Gets information about this collaboration.
063     * @return info about this collaboration.
064     */
065    public Info getInfo() {
066        BoxAPIConnection api = this.getAPI();
067        URL url = COLLABORATION_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
068
069        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
070        BoxJSONResponse response = (BoxJSONResponse) request.send();
071        JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
072        return new Info(jsonObject);
073    }
074
075    /**
076     * Updates the information about this collaboration with any info fields that have been modified locally.
077     * @param info the updated info.
078     */
079    public void updateInfo(Info info) {
080        BoxAPIConnection api = this.getAPI();
081        URL url = COLLABORATION_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
082
083        BoxJSONRequest request = new BoxJSONRequest(api, url, "PUT");
084        request.setBody(info.getPendingChanges());
085        BoxAPIResponse boxAPIResponse = request.send();
086
087        if (boxAPIResponse instanceof BoxJSONResponse) {
088            BoxJSONResponse response = (BoxJSONResponse) boxAPIResponse;
089            JsonObject jsonObject = JsonObject.readFrom(response.getJSON());
090            info.update(jsonObject);
091        }
092    }
093
094    /**
095     * Deletes this collaboration.
096     */
097    public void delete() {
098        BoxAPIConnection api = this.getAPI();
099        URL url = COLLABORATION_URL_TEMPLATE.build(api.getBaseURL(), this.getID());
100
101        BoxAPIRequest request = new BoxAPIRequest(api, url, "DELETE");
102        BoxAPIResponse response = request.send();
103        response.disconnect();
104    }
105
106    /**
107     * Contains information about a BoxCollaboration.
108     */
109    public class Info extends BoxResource.Info {
110        private BoxUser.Info createdBy;
111        private Date createdAt;
112        private Date modifiedAt;
113        private Date expiresAt;
114        private Status status;
115        private BoxCollaborator.Info accessibleBy;
116        private Role role;
117        private Date acknowledgedAt;
118        private BoxFolder.Info item;
119
120        /**
121         * Constructs an empty Info object.
122         */
123        public Info() {
124            super();
125        }
126
127        /**
128         * Constructs an Info object by parsing information from a JSON string.
129         * @param  json the JSON string to parse.
130         */
131        public Info(String json) {
132            super(json);
133        }
134
135        Info(JsonObject jsonObject) {
136            super(jsonObject);
137        }
138
139        /**
140         * Gets the user who created the collaboration.
141         * @return the user who created the collaboration.
142         */
143        public BoxUser.Info getCreatedBy() {
144            return this.createdBy;
145        }
146
147        /**
148         * Gets the time the collaboration was created.
149         * @return the time the collaboration was created.
150         */
151        public Date getCreatedAt() {
152            return this.createdAt;
153        }
154
155        /**
156         * Gets the time the collaboration was last modified.
157         * @return the time the collaboration was last modified.
158         */
159        public Date getModifiedAt() {
160            return this.modifiedAt;
161        }
162
163        /**
164         * Gets the time the collaboration will expire.
165         * @return the time the collaboration will expire.
166         */
167        public Date getExpiresAt() {
168            return this.expiresAt;
169        }
170
171        /**
172         * Gets the status of the collaboration.
173         * @return the status of the collaboration.
174         */
175        public Status getStatus() {
176            return this.status;
177        }
178
179        /**
180         * Sets the status of the collaboration in order to accept or reject the collaboration if it's pending.
181         * @param status the new status of the collaboration.
182         */
183        public void setStatus(Status status) {
184            this.status = status;
185            this.addPendingChange("status", status.name().toLowerCase());
186        }
187
188        /**
189         * Gets the collaborator who this collaboration applies to.
190         * @return the collaborator who this collaboration applies to.
191         */
192        public BoxCollaborator.Info getAccessibleBy() {
193            return this.accessibleBy;
194        }
195
196        /**
197         * Gets the level of access the collaborator has.
198         * @return the level of access the collaborator has.
199         */
200        public Role getRole() {
201            return this.role;
202        }
203
204        /**
205         * Sets the level of access the collaborator has.
206         * @param role the new level of access to give the collaborator.
207         */
208        public void setRole(Role role) {
209            this.role = role;
210            this.addPendingChange("role", role.toJSONString());
211        }
212
213        /**
214         * Gets the time the collaboration's status was changed.
215         * @return the time the collaboration's status was changed.
216         */
217        public Date getAcknowledgedAt() {
218            return this.acknowledgedAt;
219        }
220
221        /**
222         * Gets the folder the collaboration is related to.
223         * @return the folder the collaboration is related to.
224         */
225        public BoxFolder.Info getItem() {
226            return this.item;
227        }
228
229        @Override
230        public BoxCollaboration getResource() {
231            return BoxCollaboration.this;
232        }
233
234        @Override
235        protected void parseJSONMember(JsonObject.Member member) {
236            super.parseJSONMember(member);
237
238            String memberName = member.getName();
239            JsonValue value = member.getValue();
240            try {
241                if (memberName.equals("created_by")) {
242                    JsonObject userJSON = value.asObject();
243                    if (this.createdBy == null) {
244                        String userID = userJSON.get("id").asString();
245                        BoxUser user = new BoxUser(getAPI(), userID);
246                        this.createdBy = user.new Info(userJSON);
247                    } else {
248                        this.createdBy.update(userJSON);
249                    }
250
251                } else if (memberName.equals("created_at")) {
252                    this.createdAt = BoxDateFormat.parse(value.asString());
253
254                } else if (memberName.equals("modified_at")) {
255                    this.modifiedAt = BoxDateFormat.parse(value.asString());
256
257                } else if (memberName.equals("expires_at")) {
258                    this.expiresAt = BoxDateFormat.parse(value.asString());
259
260                } else if (memberName.equals("status")) {
261                    String statusString = value.asString().toUpperCase();
262                    this.status = Status.valueOf(statusString);
263
264                } else if (memberName.equals("accessible_by")) {
265                    JsonObject accessibleByJSON = value.asObject();
266                    if (this.accessibleBy == null) {
267                        this.accessibleBy = this.parseAccessibleBy(accessibleByJSON);
268                    } else {
269                        this.updateAccessibleBy(accessibleByJSON);
270                    }
271                } else if (memberName.equals("role")) {
272                    this.role = Role.fromJSONString(value.asString());
273
274                } else if (memberName.equals("acknowledged_at")) {
275                    this.acknowledgedAt = BoxDateFormat.parse(value.asString());
276
277                } else if (memberName.equals("item")) {
278                    JsonObject folderJSON = value.asObject();
279                    if (this.item == null) {
280                        String folderID = folderJSON.get("id").asString();
281                        BoxFolder folder = new BoxFolder(getAPI(), folderID);
282                        this.item = folder.new Info(folderJSON);
283                    } else {
284                        this.item.update(folderJSON);
285                    }
286                }
287            } catch (ParseException e) {
288                assert false : "A ParseException indicates a bug in the SDK.";
289            }
290        }
291
292        private void updateAccessibleBy(JsonObject json) {
293            String type = json.get("type").asString();
294            if ((type.equals("user") && this.accessibleBy instanceof BoxUser.Info)
295                || (type.equals("group") && this.accessibleBy instanceof BoxGroup.Info)) {
296
297                this.accessibleBy.update(json);
298            } else {
299                this.accessibleBy = this.parseAccessibleBy(json);
300            }
301        }
302
303        private BoxCollaborator.Info parseAccessibleBy(JsonObject json) {
304            String id = json.get("id").asString();
305            String type = json.get("type").asString();
306            BoxCollaborator.Info parsedInfo = null;
307            if (type.equals("user")) {
308                BoxUser user = new BoxUser(getAPI(), id);
309                parsedInfo = user.new Info(json);
310            } else if (type.equals("group")) {
311                BoxGroup group = new BoxGroup(getAPI(), id);
312                parsedInfo = group.new Info(json);
313            }
314
315            return parsedInfo;
316        }
317    }
318
319    /**
320     * Enumerates the possible statuses that a collaboration can have.
321     */
322    public enum Status {
323        /**
324         * The collaboration has been accepted.
325         */
326        ACCEPTED,
327
328        /**
329         * The collaboration is waiting to be accepted or rejected.
330         */
331        PENDING,
332
333        /**
334         * The collaboration has been rejected.
335         */
336        REJECTED;
337    }
338
339    /**
340     * Enumerates the possible access levels that a collaborator can have.
341     */
342    public enum Role {
343        /**
344         * An Editor has full read/write access to a folder. Once invited to a folder, they will be able to view,
345         * download, upload, edit, delete, copy, move, rename, generate shared links, make comments, assign tasks,
346         * create tags, and invite/remove collaborators. They will not be able to delete or move root level folders.
347         */
348        EDITOR ("editor"),
349
350        /**
351         * The viewer role has full read access to a folder. Once invited to a folder, they will be able to preview,
352         * download, make comments, and generate shared links.  They will not be able to add tags, invite new
353         * collaborators, upload, edit, or delete items in the folder.
354         */
355        VIEWER ("viewer"),
356
357        /**
358         * The previewer role has limited read access to a folder. They will only be able to preview the items in the
359         * folder using the integrated content viewer. They will not be able to share, upload, edit, or delete any
360         * content. This role is only available to enterprise accounts.
361         */
362        PREVIEWER ("previewer"),
363
364        /**
365         * The uploader has limited write access to a folder. They will only be able to upload and see the names of the
366         * items in a folder. They will not able to download or view any content. This role is only available to
367         * enterprise accounts.
368         */
369        UPLOADER ("uploader"),
370
371        /**
372         * The previewer-uploader role is a combination of previewer and uploader. A user with this access level will be
373         * able to preview files using the integrated content viewer as well as upload items into the folder. They will
374         * not be able to download, edit, or share, items in the folder. This role is only available to enterprise
375         * accounts.
376         */
377        PREVIEWER_UPLOADER ("previewer uploader"),
378
379        /**
380         * The viewer-uploader role is a combination of viewer and uploader. A viewer-uploader has full read access to a
381         * folder and limited write access. They are able to preview, download, add comments, generate shared links, and
382         * upload content to the folder. They will not be able to add tags, invite new collaborators, edit, or delete
383         * items in the folder. This role is only available to enterprise accounts.
384         */
385        VIEWER_UPLOADER ("viewer uploader"),
386
387        /**
388         * The co-owner role has all of the functional read/write access that an editor does. This permission level has
389         * the added ability of being able to manage users in the folder. A co-owner can add new collaborators, change
390         * access levels of existing collaborators, and remove collaborators. However, they will not be able to
391         * manipulate the owner of the folder or transfer ownership to another user. This role is only available to
392         * enterprise accounts.
393         */
394        CO_OWNER ("co-owner"),
395
396        /**
397         * The owner role has all of the functional capabilities of a co-owner. However, they will be able to manipulate
398         * the owner of the folder or transfer ownership to another user. This role is only available to enterprise
399         * accounts.
400         */
401        OWNER ("owner");
402
403        private final String jsonValue;
404
405        private Role(String jsonValue) {
406            this.jsonValue = jsonValue;
407        }
408
409        static Role fromJSONString(String jsonValue) {
410            if (jsonValue.equals("editor")) {
411                return EDITOR;
412            } else if (jsonValue.equals("viewer")) {
413                return VIEWER;
414            } else if (jsonValue.equals("previewer")) {
415                return PREVIEWER;
416            } else if (jsonValue.equals("uploader")) {
417                return UPLOADER;
418            } else if (jsonValue.equals("previewer uploader")) {
419                return PREVIEWER_UPLOADER;
420            } else if (jsonValue.equals("viewer uploader")) {
421                return VIEWER_UPLOADER;
422            } else if (jsonValue.equals("co-owner")) {
423                return CO_OWNER;
424            } else if (jsonValue.equals("owner")) {
425                return OWNER;
426            } else {
427                throw new IllegalArgumentException("The provided JSON value isn't a valid Role.");
428            }
429        }
430
431        String toJSONString() {
432            return this.jsonValue;
433        }
434    }
435}