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