1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.exoplatform.calendar.ws;
19
20 import com.sun.syndication.feed.synd.*;
21 import com.sun.syndication.io.SyndFeedInput;
22 import com.sun.syndication.io.SyndFeedOutput;
23 import com.sun.syndication.io.XmlReader;
24 import net.fortuna.ical4j.data.CalendarOutputter;
25 import net.fortuna.ical4j.model.property.CalScale;
26 import net.fortuna.ical4j.model.property.Method;
27 import net.fortuna.ical4j.model.property.ProdId;
28 import net.fortuna.ical4j.model.property.Version;
29 import org.apache.commons.fileupload.FileItem;
30 import org.apache.commons.lang.StringUtils;
31 import org.exoplatform.calendar.model.Event;
32 import org.exoplatform.calendar.service.*;
33 import org.exoplatform.calendar.service.Calendar;
34 import org.exoplatform.calendar.service.Calendar.Type;
35 import org.exoplatform.calendar.service.impl.MailNotification;
36 import org.exoplatform.calendar.ws.bean.*;
37 import org.exoplatform.calendar.ws.common.Resource;
38 import org.exoplatform.calendar.ws.common.RestAPIConstants;
39 import org.exoplatform.common.http.HTTPStatus;
40 import org.exoplatform.commons.utils.DateUtils;
41 import org.exoplatform.commons.utils.ISO8601;
42 import org.exoplatform.commons.utils.ListAccess;
43 import org.exoplatform.commons.utils.MimeTypeResolver;
44 import org.exoplatform.container.ExoContainerContext;
45 import org.exoplatform.container.xml.InitParams;
46 import org.exoplatform.container.xml.ValueParam;
47 import org.exoplatform.services.log.ExoLogger;
48 import org.exoplatform.services.log.Log;
49 import org.exoplatform.services.mail.MailService;
50 import org.exoplatform.services.organization.Group;
51 import org.exoplatform.services.organization.OrganizationService;
52 import org.exoplatform.services.rest.resource.ResourceContainer;
53 import org.exoplatform.services.security.ConversationState;
54 import org.exoplatform.services.security.Identity;
55 import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
56 import org.exoplatform.social.core.manager.IdentityManager;
57 import org.exoplatform.social.core.profile.ProfileFilter;
58 import org.exoplatform.upload.UploadResource;
59 import org.exoplatform.upload.UploadService;
60 import org.exoplatform.webservice.cs.bean.End;
61 import org.exoplatform.ws.frameworks.json.impl.JsonGeneratorImpl;
62 import org.exoplatform.ws.frameworks.json.value.JsonValue;
63 import org.json.JSONException;
64 import org.json.JSONObject;
65
66 import javax.annotation.security.RolesAllowed;
67 import javax.jcr.query.Query;
68 import javax.ws.rs.*;
69 import javax.ws.rs.core.*;
70 import javax.ws.rs.core.Response.ResponseBuilder;
71 import java.io.ByteArrayInputStream;
72 import java.io.ByteArrayOutputStream;
73 import java.io.FileInputStream;
74 import java.io.InputStream;
75 import java.io.OutputStream;
76 import java.io.Serializable;
77 import java.net.URI;
78 import java.security.MessageDigest;
79 import java.util.*;
80 import java.util.stream.Collectors;
81 import java.util.stream.Stream;
82
83
84 import io.swagger.annotations.Api;
85 import io.swagger.annotations.ApiOperation;
86 import io.swagger.annotations.ApiParam;
87 import io.swagger.annotations.ApiResponse;
88 import io.swagger.annotations.ApiResponses;
89
90
91
92
93 @Path(CalendarRestApi.CAL_BASE_URI)
94 @Api(value = CalendarRestApi.CAL_BASE_URI, description = "Entry point for calendar resources")
95 public class CalendarRestApi implements ResourceContainer {
96
97 public final static String CAL_BASE_URI = RestAPIConstants.BASE_VERSION_URI + "/calendar";
98
99 public static final String TEXT_ICS = "text/calendar";
100 public static final MediaType TEXT_ICS_TYPE = new MediaType("text","calendar");
101
102
103 public final static String BASE_URL = "/cs/calendar";
104 public final static String BASE_EVENT_URL = BASE_URL + "/event";
105
106
107 public final static String CALENDAR_URI = "/calendars/";
108 public final static String EVENT_URI = "/events/";
109 public final static String TASK_URI = "/tasks/";
110 public final static String ICS_URI = "/ics";
111 public final static String ATTACHMENT_URI = "/attachments/";
112 public final static String OCCURRENCE_URI = "/occurrences";
113 public final static String CATEGORY_URI = "/categories/";
114 public final static String PARTICIPANT_URI = "/participants/";
115 public final static String AVAILABILITY_URI = "/availabilities/";
116 public final static String FEED_URI = "/feeds/";
117 public final static String RSS_URI = "/rss";
118 public final static String INVITATION_URI ="/invitations/";
119 public static final String HEADER_LINK = "Link";
120 public static final String HEADER_LOCATION = "Location";
121
122 private OrganizationService orgService;
123 private IdentityManager identityManager;
124 private UploadService uploadService;
125 private MailService mailService;
126
127 private int defaultLimit = 10;
128 private int hardLimit = 100;
129
130 private SubResourceHrefBuilder subResourcesBuilder = new SubResourceHrefBuilder(this);
131
132 private final static CacheControl nc = new CacheControl();
133
134 public static final String DEFAULT_CAL_NAME = "calendar";
135
136 public static final String DEFAULT_EVENT_NAME = "default";
137
138 public static final String[] RP_WEEKLY_BYDAY = CalendarEvent.RP_WEEKLY_BYDAY.clone();
139
140 public static final String[] EVENT_AVAILABILITY = {CalendarEvent.ST_AVAILABLE, CalendarEvent.ST_BUSY, CalendarEvent.ST_OUTSIDE};
141
142 public static final String[] REPEATTYPES = CalendarEvent.REPEATTYPES.clone();
143
144 public final static String RP_END_BYDATE = "endByDate";
145
146 public final static String RP_END_AFTER = "endAfter";
147
148 public final static String RP_END_NEVER = "neverEnd";
149
150 public static final String[] PRIORITY = CalendarEvent.PRIORITY.clone();
151
152 public static final String[] TASK_STATUS = CalendarEvent.TASK_STATUS.clone();
153
154 private static final String[] INVITATION_STATUS = {"", "maybe", "yes", "no"};
155
156 public static enum RecurringUpdateType {
157 ALL, FOLLOWING, ONE
158 }
159
160 static {
161 Arrays.sort(RP_WEEKLY_BYDAY);
162 Arrays.sort(EVENT_AVAILABILITY);
163 Arrays.sort(REPEATTYPES);
164 Arrays.sort(PRIORITY);
165 Arrays.sort(TASK_STATUS);
166 Arrays.sort(INVITATION_STATUS);
167 }
168
169 private final CacheControl cc = new CacheControl();
170
171 static {
172 nc.setNoCache(true);
173 nc.setNoStore(true);
174 }
175
176 private static final Log log = ExoLogger.getExoLogger(CalendarRestApi.class);
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 public CalendarRestApi(OrganizationService orgService, IdentityManager identityManager, UploadService uploadService, MailService mailService, InitParams params) {
193 this.orgService = orgService;
194 this.identityManager = identityManager;
195 this.uploadService = uploadService;
196 this.mailService = mailService;
197
198 int maxAge = 604800;
199 if (params != null) {
200 if (params.getValueParam("default.limit") != null) {
201 defaultLimit = Integer.parseInt(params.getValueParam("default.limit").getValue());
202 }
203 if (params.getValueParam("hard.limit") != null) {
204 hardLimit = Integer.parseInt(params.getValueParam("hard.limit").getValue());
205 }
206
207 ValueParam cacheConfig = params.getValueParam("cache_maxage");
208 if (cacheConfig != null) {
209 try {
210 maxAge = Integer.parseInt(cacheConfig.getValue());
211 } catch (Exception ex) {
212 log.warn("Can't parse {} to maxAge, use the default value {}", cacheConfig, maxAge);
213 }
214 }
215 }
216 cc.setPrivate(true);
217 cc.setMaxAge(maxAge);
218 cc.setSMaxAge(maxAge);
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 @GET
244 @RolesAllowed("users")
245 @Produces(MediaType.APPLICATION_JSON)
246 @ApiOperation(
247 value = "Returns all the available subresources as json",
248 notes = "Returns all the available subresources as json, in order to navigate easily in the REST API.")
249 @ApiResponses(value = {
250 @ApiResponse(code = 200, message = "Successful retrieval of all available subresources")
251 })
252 public Response getSubResources(@Context UriInfo uri) {
253 Map<String, String[]> subResources = new HashMap<String, String[]>();
254 subResources.put("subResourcesHref", subResourcesBuilder.buildResourceMap(uri));
255
256 return Response.ok(subResources, MediaType.APPLICATION_JSON).cacheControl(nc).build();
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 @SuppressWarnings({ "unchecked", "rawtypes" })
331 @GET
332 @RolesAllowed("users")
333 @Path("/calendars/")
334 @Produces({MediaType.APPLICATION_JSON})
335 @ApiOperation(
336 value = "Returns all user-related calendars",
337 notes = "This method lists all the calendars a specific user can see.")
338 @ApiResponses(value = {
339 @ApiResponse(code = 200, message = "Successful retrieval of all user-related calendars"),
340 @ApiResponse(code = 404, message = "Bad Request, or no calendars associated to the user"),
341 @ApiResponse(code = 503, message = "Can't generate JSON file") })
342 public Response getCalendars(
343 @ApiParam(value = "The calendar type to search for. It can be one of \"personal, group, shared\"", required = false, allowableValues = "personal, group, shared") @QueryParam("type") String type,
344 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
345 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
346 @ApiParam(value = "Tell the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
347 @ApiParam(value = "This is a list of comma-separated property's names of response json object", required = false) @QueryParam("fields") String fields,
348 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
349 @Context UriInfo uri) {
350 try {
351 limit = parseLimit(limit);
352 Type calType = Calendar.Type.UNDEFINED;
353
354 if (type != null) {
355 try {
356 calType = Calendar.Type.valueOf(type.toUpperCase());
357 } catch (IllegalArgumentException ex) {
358
359 log.debug(ex);
360 }
361 }
362
363 CalendarCollection<Calendar> cals = calendarServiceInstance().getAllCalendars(currentUserId(), calType.type(), offset, limit);
364 if(cals == null || cals.isEmpty()) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
365
366 String basePath = getBasePath(uri);
367 Collection data = new LinkedList();
368 Iterator<Calendar> calIterator = cals.iterator();
369 while (calIterator.hasNext()) {
370 Calendar cal = calIterator.next();
371 setCalType(cal);
372 data.add(extractObject(new CalendarResource(cal, basePath), fields));
373 }
374
375 CollectionResource calData = new CollectionResource(data, returnSize ? cals.getFullSize() : -1);
376 calData.setOffset(offset);
377 calData.setLimit(limit);
378
379 ResponseBuilder okResult;
380 if (jsonp != null) {
381 JsonValue value = new JsonGeneratorImpl().createJsonObject(calData);
382 StringBuilder sb = new StringBuilder(jsonp);
383 sb.append("(").append(value).append(");");
384 okResult = Response.ok(sb.toString(), new MediaType("text", "javascript"));
385 } else {
386 okResult = Response.ok(calData, MediaType.APPLICATION_JSON);
387 }
388
389 if (returnSize) {
390 okResult.header(HEADER_LINK, buildFullUrl(uri, offset, limit, calData.getSize()));
391 }
392
393
394 return okResult.cacheControl(nc).build();
395 } catch (Exception e) {
396 if(log.isDebugEnabled()) log.debug(e.getMessage());
397 }
398 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
399
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427 @POST
428 @RolesAllowed("users")
429 @Path("/calendars/")
430 @ApiOperation(
431 value = "Creates a calendar",
432 notes = "Creates a calendar if: <br/>"
433 + "- this is a personal calendar and the user is authenticated<br/>"
434 + "- this is a group calendar and the user is authenticated and belongs to the group."
435 )
436 @ApiResponses(value = {
437 @ApiResponse(code = 201, message = "Calendar successfully created"),
438 @ApiResponse(code = 401, message = "The User isn't authorized to create a calendar there")
439 })
440 public Response createCalendar(CalendarResource cal, @Context UriInfo uriInfo) {
441 Calendar calendar = new Calendar();
442 if (cal.getName() == null) {
443 cal.setName(DEFAULT_CAL_NAME);
444 }
445 if (cal.getOwner() == null) {
446 cal.setOwner(currentUserId());
447 }
448 Response error = buildCalendar(calendar, cal);
449 if (error != null) {
450 return error;
451 }
452
453 if (cal.getGroups() != null && cal.getGroups().length > 0) {
454
455 if (isInGroups(cal.getGroups())) {
456 calendarServiceInstance().savePublicCalendar(calendar, true);
457 } else {
458 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
459 }
460 } else {
461 if (cal.getOwner() != null && !cal.getOwner().equals(currentUserId())) {
462 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
463 } else {
464
465 final String username = currentUserId();
466 calendarServiceInstance().saveUserCalendar(username, calendar, true);
467
468
469 String[] viewPermissions = calendar.getViewPermission();
470 if (viewPermissions != null && viewPermissions.length > 0) {
471 Set<String> sharedUsers = new HashSet<String>();
472 Set<String> sharedGroups = new HashSet<String>();
473
474 for (String permission : viewPermissions) {
475 PermissionOwner perm = PermissionOwner.createPermissionOwnerFrom(permission);
476 if (PermissionOwner.USER_OWNER.equals(perm.getOwnerType())) {
477 sharedUsers.add(perm.getId());
478
479 } else if (PermissionOwner.GROUP_OWNER.equals(perm.getOwnerType())) {
480 sharedGroups.add(perm.getGroupId());
481
482 } else if (PermissionOwner.MEMBERSHIP_OWNER.equals(perm.getOwnerType())) {
483 try {
484 sharedUsers.addAll(Utils.getUserByMembershipId(perm.getMembership(), perm.getGroupId()));
485
486 } catch (Exception ex) {
487 log.warn("Can not share calendar to Membership: " + permission, ex);
488 }
489 }
490 }
491
492 if (sharedGroups.size() > 0) {
493 try {
494 calendarServiceInstance().shareCalendarByRunJob(username, calendar.getId(), new ArrayList<String>(sharedGroups));
495 } catch (Exception ex) {
496 log.warn("Exception while share calendar to groups", ex);
497 }
498 }
499
500 if (sharedUsers.size() > 0) {
501 try {
502 calendarServiceInstance().shareCalendar(username, calendar.getId(), new ArrayList<String>(sharedUsers));
503 } catch (Exception ex) {
504 log.warn("Exception while share calendar to users", ex);
505 }
506 }
507
508 }
509 }
510 }
511 StringBuilder location = new StringBuilder(getBasePath(uriInfo));
512 location.append(CALENDAR_URI);
513 location.append(calendar.getId());
514 return Response.status(HTTPStatus.CREATED).header(HEADER_LOCATION, location).cacheControl(nc).build();
515 }
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 @GET
560 @RolesAllowed("users")
561 @Path("/calendars/{id}")
562 @Produces(MediaType.APPLICATION_JSON)
563 @ApiOperation(
564 value = "Finds a calendar by ID",
565 notes = "Returns the calendar with the specified id parameter if:<br/>"
566 + "- The authenticated user is the owner of the calendar<br/>"
567 + "- The authenticated user belongs to the group of the calendar<br/>"
568 + "- The calendar has been shared with the authenticated user or with a group of the authenticated user")
569 @ApiResponses(value = {
570 @ApiResponse(code = 200, message = "Successful retrieval of the calendar"),
571 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
572 @ApiResponse(code = 503, message = "Can't generate JSON file")
573 })
574 public Response getCalendarById(
575 @ApiParam(value = "Identity of the calendar to retrieve", required = true) @PathParam("id") String id,
576 @ApiParam(value = "This is a list of comma-separated property's names of response json object", required = false) @QueryParam("fields") String fields,
577 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
578 @Context UriInfo uriInfo,
579 @Context Request request) {
580 try {
581 CalendarService service = calendarServiceInstance();
582 Calendar cal = service.getCalendarById(id);
583 if(cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
584 cal.setCalType(service.getTypeOfCalendar(currentUserId(), cal.getId()));
585
586 Date lastModified = new Date(cal.getLastModified());
587 ResponseBuilder preCondition = request.evaluatePreconditions(lastModified);
588 if (preCondition != null) {
589 return preCondition.build();
590 }
591
592 CalendarResource calData = null;
593 if (this.hasViewCalendarPermission(cal, currentUserId())) {
594 setCalType(cal);
595 calData = new CalendarResource(cal, getBasePath(uriInfo));
596 }
597 if (calData == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
598
599 Object resource = extractObject(calData, fields);
600 if (jsonp != null) {
601 String json = null;
602 if (resource instanceof Map) json = new JSONObject(resource).toString();
603 else {
604 JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl();
605 json = generatorImpl.createJsonObject(resource).toString();
606 }
607 StringBuilder sb = new StringBuilder(jsonp);
608 sb.append("(").append(json).append(");");
609 return Response.ok(sb.toString(), new MediaType("text", "javascript")).cacheControl(cc).lastModified(lastModified).build();
610 }
611
612
613 return Response.ok(resource, MediaType.APPLICATION_JSON).cacheControl(cc).lastModified(lastModified).build();
614 } catch (Exception e) {
615 if(log.isDebugEnabled()) log.debug(e.getMessage());
616 }
617 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
618 }
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645 @PUT
646 @RolesAllowed("users")
647 @Path("/calendars/{id}")
648 @ApiOperation(
649 value = "Updates a calendar",
650 notes = "Update the calendar with specified id if:<br/>"
651 + "- the authenticated user is the owner of the calendar<br/>"
652 + "- for group calendars, the authenticated user has edit rights on the calendar")
653 @ApiResponses(value = {
654 @ApiResponse(code = 200, message = "Calendar successfully updated"),
655 @ApiResponse(code = 401, message = "User unauthorized to update the calendar"),
656 @ApiResponse(code = 403, message = "If user try to update invalid data to the calendar"),
657 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
658 @ApiResponse(code = 503, message = "An error occurred during the saving process")
659 })
660 public Response updateCalendarById(
661 @ApiParam(value = "Identity of the calendar to update", required = true) @PathParam("id") String id,
662 CalendarResource calObj) {
663 try {
664 Calendar cal = calendarServiceInstance().getCalendarById(id);
665 if(cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
666
667
668
669 if ((currentUserId().equals(cal.getCalendarOwner()) || cal.getGroups() != null) &&
670 Utils.isCalendarEditable(currentUserId(), cal)) {
671
672 final List<String> oldViewPermissions;
673 if (cal.getViewPermission() != null) {
674 oldViewPermissions = Collections.unmodifiableList(Arrays.<String>asList(cal.getViewPermission()));
675 } else {
676 oldViewPermissions = Collections.<String>emptyList();
677 }
678
679 Response error = buildCalendar(cal, calObj);
680 if (error != null) {
681 return error;
682 } else {
683 int type = calendarServiceInstance().getTypeOfCalendar(currentUserId(), cal.getId());
684
685 if (type == Calendar.TYPE_PRIVATE) {
686 if (!currentUserId().equals(cal.getCalendarOwner())) {
687 return Response.status(HTTPStatus.FORBIDDEN).entity("Can not change owner of personal calendar").cacheControl(nc).build();
688 }
689 if (cal.getGroups() != null && cal.getGroups().length > 0) {
690 return Response.status(HTTPStatus.FORBIDDEN).entity("Can not update groups of personal calendar").cacheControl(nc).build();
691 }
692 }
693
694 calendarServiceInstance().saveCalendar(cal.getCalendarOwner(), cal, type, false);
695
696 if (type == Calendar.TYPE_PRIVATE) {
697 final List<String> viewPermissions;
698 if (cal.getViewPermission() != null) {
699 viewPermissions = Arrays.asList(cal.getViewPermission());
700 } else {
701 viewPermissions = Collections.emptyList();
702 }
703
704
705 boolean needUpdateShare = false;
706 if (oldViewPermissions.size() != viewPermissions.size()) {
707 needUpdateShare = true;
708 } else {
709 for (String p : oldViewPermissions) {
710 if (!viewPermissions.contains(p)) {
711 needUpdateShare = true;
712 break;
713 }
714 }
715 }
716
717 if (needUpdateShare) {
718 final String username = currentUserId();
719 final String calendarId = cal.getId();
720 Set<String> newSharedUsers = new HashSet<String>();
721 Set<String> newSharedGroups = new HashSet<String>();
722 for (String p : viewPermissions) {
723 PermissionOwner perm = PermissionOwner.createPermissionOwnerFrom(p);
724 String ownerType = perm.getOwnerType();
725 if (PermissionOwner.USER_OWNER.equals(ownerType)) {
726 newSharedUsers.add(perm.getId());
727 } else if (PermissionOwner.GROUP_OWNER.equals(ownerType)) {
728 newSharedGroups.add(perm.getGroupId());
729 } else if (PermissionOwner.MEMBERSHIP_OWNER.equals(ownerType)) {
730 try {
731 newSharedUsers.addAll(Utils.getUserByMembershipId(perm.getMembership(), perm.getGroupId()));
732 } catch (Exception ex) {
733 log.warn("Exception while try to share calendar to Membership: " + p, ex);
734 }
735 }
736 }
737
738 Set<String> removeShareUsers = new HashSet<String>();
739 Set<String> removeShareGroups = new HashSet<String>();
740 if (oldViewPermissions.size() > 0) {
741 for (String p : oldViewPermissions) {
742 if (viewPermissions.contains(p)) {
743 continue;
744 }
745
746 PermissionOwner perm = PermissionOwner.createPermissionOwnerFrom(p);
747 String ownerType = perm.getOwnerType();
748 if (PermissionOwner.USER_OWNER.equals(ownerType)) {
749 removeShareUsers.add(perm.getId());
750 } else if (PermissionOwner.GROUP_OWNER.equals(ownerType)) {
751 removeShareGroups.add(perm.getGroupId());
752 } else if (PermissionOwner.MEMBERSHIP_OWNER.equals(ownerType)) {
753 try {
754 removeShareUsers.addAll(Utils.getUserByMembershipId(perm.getMembership(), perm.getGroupId()));
755 } catch (Exception ex) {
756 log.error("Exception when try unshare calendar to Membership: " + p, ex);
757 }
758 }
759 }
760 }
761
762
763 for (String user : removeShareUsers) {
764 calendarServiceInstance().removeSharedCalendar(user, calendarId);
765 }
766 if (removeShareGroups.size() > 0) {
767 calendarServiceInstance().removeSharedCalendarByJob(username,
768 new ArrayList<String>(removeShareGroups), calendarId);
769 }
770
771
772 if (newSharedUsers.size() > 0) {
773 newSharedUsers.remove(username);
774 calendarServiceInstance().shareCalendar(username, calendarId, new ArrayList<String>(newSharedUsers));
775 }
776 if (newSharedGroups.size() > 0) {
777 calendarServiceInstance().shareCalendarByRunJob(username,
778 calendarId, new ArrayList<String>(newSharedGroups));
779 }
780 }
781 }
782
783 return Response.ok().cacheControl(nc).build();
784 }
785 }
786
787
788 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
789 } catch (Exception e) {
790 log.error(e);
791 }
792 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
793 }
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814 @DELETE
815 @RolesAllowed("users")
816 @Path("/calendars/{id}")
817 @ApiOperation(
818 value = "Deletes a calendar",
819 notes = "Delete the calendar with the specified id if:<br/>"
820 + "- the authenticated user is the owner of the calendar.<br/>"
821 + "- for group calendars, the authenticated user has edit rights on the calendar.<br/>"
822 + "- If it is a shared calendar the calendar is not shared anymore (but the original calendar is not deleted).")
823 @ApiResponses(value = {
824 @ApiResponse(code = 200, message = "Calendar successfully deleted"),
825 @ApiResponse(code = 401, message = "User unauthorized to delete the calendar"),
826 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
827 @ApiResponse(code = 503, message = "An error occurred during the saving process")
828 })
829 public Response deleteCalendarById(
830 @ApiParam(value = "Identity of the calendar to delete", required = true) @PathParam("id") String id) {
831 try {
832 Calendar cal = calendarServiceInstance().getCalendarById(id);
833 if(cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
834
835 cal.setCalType(calendarServiceInstance().getTypeOfCalendar(currentUserId(), id));
836 if (Utils.isCalendarEditable(currentUserId(), cal) || cal.getCalType() == Calendar.TYPE_SHARED) {
837 switch (cal.getCalType()) {
838 case Calendar.TYPE_PRIVATE:
839 calendarServiceInstance().removeUserCalendar(cal.getCalendarOwner(), id);
840 break;
841 case Calendar.TYPE_PUBLIC:
842 calendarServiceInstance().removePublicCalendar(id);
843 break;
844 case Calendar.TYPE_SHARED:
845 if (this.hasViewCalendarPermission(cal, currentUserId())) {
846 calendarServiceInstance().removeSharedCalendar(currentUserId(),id);
847 break;
848 }
849 }
850 return Response.ok().cacheControl(nc).build();
851 } else {
852 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
853 }
854 } catch (Exception e) {
855 if(log.isDebugEnabled()) log.debug(e.getMessage());
856 }
857 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
858 }
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881 @GET
882 @RolesAllowed("users")
883 @Path("/calendars/{id}/ics")
884 @Produces(TEXT_ICS)
885 @ApiOperation(
886 value = "Exports a calendar to iCal",
887 notes = "Returns an iCalendar formated file which is exported from the calendar with specified id if:<br/>"
888 + "- the calendar is public<br/>"
889 + "- the authenticated user is the owner of the calendar<br/>"
890 + "- the authenticated user belongs to the group of the calendar<br/>"
891 + "- the calendar has been shared with the authenticated user or with a group of the authenticated user")
892 @ApiResponses(value = {
893 @ApiResponse(code = 200, message = "Calendar successfully exported to ICS"),
894 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
895 @ApiResponse(code = 503, message = "An error occurred")
896 })
897 public Response exportCalendarToIcs(
898 @ApiParam(value = "Identity of the calendar to retrieve ICS file", required = true) @PathParam("id") String id,
899 @Context Request request) {
900 try {
901 Calendar cal = calendarServiceInstance().getCalendarById(id);
902 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
903
904 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId())) {
905 int type = calendarServiceInstance().getTypeOfCalendar(currentUserId(),id);
906 String username = currentUserId();
907 if (type == -1) {
908
909
910 type = Calendar.TYPE_PRIVATE;
911 username = cal.getCalendarOwner();
912 }
913
914 CalendarImportExport iCalExport = calendarServiceInstance().getCalendarImportExports(CalendarService.ICALENDAR);
915 ArrayList<String> calIds = new ArrayList<String>();
916 calIds.add(id);
917 OutputStream out = iCalExport.exportCalendar(username, calIds, String.valueOf(type), Utils.UNLIMITED);
918
919
920
921
922 if (out == null) {
923 net.fortuna.ical4j.model.Calendar calendar = new net.fortuna.ical4j.model.Calendar();
924 calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));
925 calendar.getProperties().add(Version.VERSION_2_0);
926 calendar.getProperties().add(CalScale.GREGORIAN);
927 calendar.getProperties().add(Method.REQUEST);
928 out = new ByteArrayOutputStream();
929 CalendarOutputter output = new CalendarOutputter(false);
930 output.output(calendar, out);
931 }
932
933 byte[] data = out.toString().getBytes();
934
935 byte[] hashCode = digest(data).getBytes();
936 EntityTag tag = new EntityTag(new String(hashCode));
937 ResponseBuilder preCondition = request.evaluatePreconditions(tag);
938 if (preCondition != null) {
939 return preCondition.build();
940 }
941
942 InputStream in = new ByteArrayInputStream(data);
943 return Response.ok(in, TEXT_ICS_TYPE)
944 .header("Content-Disposition", "attachment;filename=\"" + cal.getName() + Utils.ICS_EXT)
945 .cacheControl(cc).tag(tag).build();
946 } else {
947 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
948 }
949 } catch (Exception e) {
950 if(log.isDebugEnabled()) log.debug(e.getMessage());
951 }
952 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
953
954 }
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 @GET
1036 @RolesAllowed("users")
1037 @Path("/events/{id}")
1038 @Produces(MediaType.APPLICATION_JSON)
1039 @ApiOperation(
1040 value = "Returns an event by ID",
1041 notes = "Returns an event with specified id parameter if:<br/>"
1042 + "- the calendar of the event is public<br/>"
1043 + "- the authenticated user is the owner of the calendar of the event<br/>"
1044 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
1045 + "- the authenticated user is a participant of the event<br/>"
1046 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user"
1047 )
1048 @ApiResponses(value = {
1049 @ApiResponse(code = 200, message = "Successful retrieval of the event"),
1050 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
1051 @ApiResponse(code = 503, message = "An error occurred")
1052 })
1053 public Response getEventById(
1054 @ApiParam(value = "Identity of the event to find", required = true) @PathParam("id") String id,
1055 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
1056 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link", required = false) @QueryParam("expand") String expand,
1057 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
1058 @Context UriInfo uriInfo,
1059 @Context Request request) {
1060 try {
1061 CalendarService service = calendarServiceInstance();
1062 CalendarEvent ev = service.getEventById(id);
1063 if(ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1064
1065 Date lastModified = new Date(ev.getLastModified());
1066 ResponseBuilder preCondition = request.evaluatePreconditions(lastModified);
1067 if (preCondition != null) {
1068 return preCondition.build();
1069 }
1070
1071 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
1072 boolean inParticipant = false;
1073 String[] participant = ev.getParticipant();
1074 if (participant != null) {
1075 Arrays.sort(participant);
1076 if (Arrays.binarySearch(participant, currentUserId()) > -1) inParticipant = true;
1077 }
1078
1079 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId()) || inParticipant) {
1080 Object resource = buildEventResource(ev, uriInfo, expand, fields);
1081 return buildJsonP(resource, jsonp).cacheControl(cc).lastModified(lastModified).build();
1082 } else {
1083 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1084 }
1085 } catch (Exception e) {
1086 if(log.isDebugEnabled()) log.debug(e.getMessage());
1087 }
1088 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1089 }
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126 @PUT
1127 @RolesAllowed("users")
1128 @Path("/events/{id}")
1129 @ApiOperation(
1130 value = "Updates an event identified by its ID",
1131 notes = "Updates the event with specified id if:<br/>"
1132 + "- the authenticated user is the owner of the calendar of the event<br/>"
1133 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
1134 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
1135 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
1136 @ApiResponses(value = {
1137 @ApiResponse(code = 200, message = "Event successfully updated"),
1138 @ApiResponse(code = 400, message = "Bad Request, parameters not valid"),
1139 @ApiResponse(code = 401, message = "User unauthorized to update the event"),
1140 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
1141 @ApiResponse(code = 503, message = "Error during the saving process")
1142 })
1143 public Response updateEventById(
1144 @ApiParam(value = "Identity of the event to update", required = true) @PathParam("id") String id,
1145 @ApiParam(value = "Recurring update type, can be ALL, FOLLOWING or ONE, by default, it's ONE", required = false) @QueryParam("recurringUpdateType") RecurringUpdateType recurringUpdateType,
1146 EventResource evObject) {
1147 try {
1148 CalendarEvent event = calendarServiceInstance().getEventById(id);
1149 if(event == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1150 if (recurringUpdateType != null) {
1151
1152 event = CalendarEvent.build(event);
1153 }
1154
1155 Calendar moveToCal = null;
1156 if (evObject.getCalendarId() != null && !event.getCalendarId().equals(evObject.getCalendarId())) {
1157 moveToCal = calendarServiceInstance().getCalendarById(evObject.getCalendarId());
1158 }
1159
1160 Calendar cal = calendarServiceInstance().getCalendarById(event.getCalendarId());
1161 int fromType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), cal.getId());
1162 if (Utils.isCalendarEditable(currentUserId(), cal) && (moveToCal == null || Utils.isCalendarEditable(currentUserId(), moveToCal))) {
1163 Response error = buildEvent(event, evObject, moveToCal);
1164 if (error != null) {
1165 return error;
1166 }
1167
1168 int toType;
1169 if(moveToCal != null) {
1170 toType = moveToCal.getCalType();
1171 } else {
1172 toType = fromType;
1173 }
1174
1175 moveToCal = moveToCal == null ? cal : moveToCal;
1176 saveEvent(cal.getId(), moveToCal.getId(), fromType, toType, event, recurringUpdateType, false);
1177
1178 return Response.ok().cacheControl(nc).build();
1179 }
1180
1181
1182 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
1183 } catch (Exception e) {
1184 if(log.isDebugEnabled()) log.debug(e.getMessage());
1185 }
1186 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1187 }
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 @DELETE
1210 @RolesAllowed("users")
1211 @Path("/events/{id}")
1212 @ApiOperation(
1213 value = "Deletes an event identified by its ID",
1214 notes = "Delete an event with specified id parameter if:<br/>"
1215 + "- the authenticated user is the owner of the calendar of the event<br/>"
1216 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
1217 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
1218 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
1219 @ApiResponses(value = {
1220 @ApiResponse(code = 200, message = "Event deleted successfully"),
1221 @ApiResponse(code = 401, message = "User unauthorized to delete this event"),
1222 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
1223 @ApiResponse(code = 503, message = "An error occurred during the saving process")
1224 })
1225 public Response deleteEventById(
1226 @ApiParam(value = "identity of the event to delete", required = true) @PathParam("id") String id) {
1227 try {
1228 CalendarEvent ev = calendarServiceInstance().getEventById(id);
1229 if(ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1230
1231 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
1232 if (Utils.isCalendarEditable(currentUserId(), cal)) {
1233 int calType = Calendar.TYPE_ALL;
1234 try {
1235 calType = Integer.parseInt(ev.getCalType());
1236 } catch (NumberFormatException e) {
1237 calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), ev.getCalendarId());
1238 }
1239 switch (calType) {
1240 case Calendar.TYPE_PRIVATE:
1241 calendarServiceInstance().removeUserEvent(currentUserId(), ev.getCalendarId(), id);
1242 break;
1243 case Calendar.TYPE_PUBLIC:
1244 calendarServiceInstance().removePublicEvent(ev.getCalendarId(),id);
1245 break;
1246 case Calendar.TYPE_SHARED:
1247 calendarServiceInstance().removeSharedEvent(currentUserId(), ev.getCalendarId(), id);
1248 break;
1249
1250 default:
1251 break;
1252 }
1253 return Response.ok().cacheControl(nc).build();
1254 } else {
1255 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
1256 }
1257 } catch (Exception e) {
1258 if(log.isDebugEnabled()) log.debug(e.getMessage());
1259 }
1260 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310 @SuppressWarnings({ "rawtypes", "unchecked" })
1311 @GET
1312 @RolesAllowed("users")
1313 @Path("/events/{id}/attachments")
1314 @Produces(MediaType.APPLICATION_JSON)
1315 @ApiOperation(
1316 value = "Returns the attachments of an event identified by its ID",
1317 notes = "Returns attachments of an event with specified id if:<br/>"
1318 + "- the calendar of the event is public<br/>"
1319 + "- the authenticated user is the owner of the calendar of the event<br/>"
1320 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
1321 + "- the authenticated user is a participant of the event<br/>"
1322 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user")
1323 @ApiResponses(value = {
1324 @ApiResponse(code = 200, message = "Successful retrieval of all attachments"),
1325 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
1326 @ApiResponse(code = 503, message = "An error occured during the saving process")
1327 })
1328 public Response getAttachmentsFromEvent(
1329 @ApiParam(value = "Identity of an event to query for attachments", required = true) @PathParam("id") String id,
1330 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
1331 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used*", required = false) @QueryParam("limit") int limit,
1332 @ApiParam(value = "This is a list of comma-separated property's names of response json object", required = false) @QueryParam("fields") String fields,
1333 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
1334 @Context UriInfo uriInfo) {
1335 try {
1336 limit = parseLimit(limit);
1337
1338 CalendarEvent ev = calendarServiceInstance().getEventById(id);
1339 if(ev == null || ev.getAttachment() == null) {
1340 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1341 } else {
1342 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
1343 boolean inParticipant = false;
1344 if (ev.getParticipant() != null) {
1345 String[] participant = ev.getParticipant();
1346 Arrays.sort(participant);
1347 int i = Arrays.binarySearch(participant, currentUserId());
1348 if (i > -1) inParticipant = true;
1349 }
1350
1351 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId()) || inParticipant) {
1352 Iterator<Attachment> it = ev.getAttachment().iterator();
1353 List attResource = new ArrayList();
1354 Utils.skip(it, offset);
1355 int counter = 0;
1356 String basePath = getBasePath(uriInfo);
1357 while (it.hasNext()) {
1358 Attachment a = it.next();
1359 attResource.add(extractObject(new AttachmentResource(a, basePath), fields));
1360 if(++counter == limit) break;
1361 }
1362 CollectionResource evData = new CollectionResource(attResource, ev.getAttachment().size());
1363 evData.setOffset(offset);
1364 evData.setLimit(limit);
1365
1366 if (jsonp != null) {
1367 JsonValue value = new JsonGeneratorImpl().createJsonObject(evData);
1368 StringBuilder sb = new StringBuilder(jsonp);
1369 sb.append("(").append(value).append(");");
1370 return Response.ok(sb.toString(), new MediaType("text", "javascript")).cacheControl(nc).header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, evData.getSize())).build();
1371 }
1372
1373
1374 return Response.ok(evData, MediaType.APPLICATION_JSON).header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, evData.getSize())).cacheControl(nc).build();
1375 }
1376
1377
1378 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1379 }
1380 } catch (Exception e) {
1381 if(log.isDebugEnabled()) log.debug(e.getMessage());
1382 }
1383 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1384 }
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412 @POST
1413 @RolesAllowed("users")
1414 @Path("/events/{id}/attachments")
1415 @Consumes("multipart/*")
1416 @ApiOperation(
1417 value = "Creates attachments for an event identified by its ID",
1418 notes = "Creates attachments for an event with specified id if:<br/>"
1419 + "- the authenticated user is the owner of the calendar of the event<br/>"
1420 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
1421 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
1422 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
1423 @ApiResponses(value = {
1424 @ApiResponse(code = 201, message = "Attachment successfully created"),
1425 @ApiResponse(code = 401, message = "User unauthorized to create an attachment to this event"),
1426 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
1427 @ApiResponse(code = 503, message = "An error occurred during the saving process")
1428 })
1429 public Response createAttachmentForEvent(
1430 @Context UriInfo uriInfo,
1431 @ApiParam(value = "Identity of an event where the attachment is created", required = true) @PathParam("id") String id,
1432 Iterator<FileItem> iter) {
1433 try {
1434 CalendarEvent event = calendarServiceInstance().getEventById(id);
1435 if (event == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1436
1437 Calendar cal = calendarServiceInstance().getCalendarById(event.getCalendarId());
1438
1439 if (Utils.isCalendarEditable(currentUserId(), cal)) {
1440 int calType = Calendar.TYPE_ALL;
1441 List<Attachment> attachment = new ArrayList<Attachment>();
1442 try {
1443 calType = Integer.parseInt(event.getCalType());
1444 } catch (NumberFormatException e) {
1445 calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), event.getCalendarId());
1446 }
1447
1448 attachment.addAll(event.getAttachment());
1449 while (iter.hasNext()) {
1450 FileItem file = iter.next();
1451 String fileName = file.getName();
1452 if(fileName != null) {
1453 String mimeType = new MimeTypeResolver().getMimeType(fileName.toLowerCase());
1454 Attachment at = new Attachment();
1455 at.setMimeType(mimeType);
1456 at.setSize(file.getSize());
1457 at.setName(file.getName());
1458 at.setInputStream(file.getInputStream());
1459 attachment.add(at);
1460 }
1461 }
1462 event.setAttachment(attachment);
1463
1464 saveEvent(calType, event, false);
1465
1466 StringBuilder attUri = new StringBuilder(getBasePath(uriInfo));
1467 attUri.append("/").append(event.getId());
1468 attUri.append(ATTACHMENT_URI);
1469 return Response.status(HTTPStatus.CREATED).header(HEADER_LOCATION, attUri.toString()).cacheControl(nc).build();
1470 }
1471
1472
1473 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
1474 } catch (Exception e) {
1475 if(log.isDebugEnabled()) log.debug(e.getMessage());
1476 }
1477 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1478 }
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582 @SuppressWarnings({ "rawtypes", "unchecked" })
1583 @GET
1584 @RolesAllowed("users")
1585 @Path("/calendars/{id}/events")
1586 @Produces(MediaType.APPLICATION_JSON)
1587 @ApiOperation(
1588 value = "Returns the events of a calendar identified by its ID",
1589 notes = "Returns events of an calendar with specified id when:<br/>"
1590 + "- the calendar is public<br/>"
1591 + "- the authenticated user is the owner of the calendar of the event<br/>"
1592 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
1593 + "- the authenticated user is a participant of the event<br/>"
1594 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user")
1595 @ApiResponses(value = {
1596 @ApiResponse(code = 200, message = "Successful retrieval of all events from the calendar"),
1597 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found")
1598 })
1599 public Response getEventsByCalendar(
1600 @ApiParam(value = "Identity of a calendar to search for events", required = true) @PathParam("id") String id,
1601 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *from* this date", required = false, defaultValue = "Current server time") @QueryParam("startTime") String start,
1602 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *to* this date", required = false, defaultValue = "Current server time + 1 week") @QueryParam("endTime") String end,
1603 @ApiParam(value = "Search for this category only. If not specified, search event of any category", required = false) @QueryParam("category") String category,
1604 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
1605 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
1606 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
1607 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
1608 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link", required = false) @QueryParam("expand") String expand,
1609 @ApiParam(value = "Tells the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
1610 @Context UriInfo uri) throws Exception {
1611 limit = parseLimit(limit);
1612 String username = currentUserId();
1613
1614 CalendarService service = calendarServiceInstance();
1615 EventDAO evtDAO = service.getEventDAO();
1616
1617 long fullSize = returnSize ? 0 : -1;
1618 List data = new LinkedList();
1619 Calendar calendar = service.getCalendarById(id);
1620
1621 if (calendar != null) {
1622 if (calendar.hasChildren()) {
1623 String participant = null;
1624 if (calendar.getPublicUrl() == null && !hasViewCalendarPermission(calendar, username)) {
1625 participant = username;
1626 }
1627
1628 EventQuery eventQuery = buildEventQuery(start, end, category, Arrays.asList(calendar),
1629 id, participant, CalendarEvent.TYPE_EVENT);
1630 ListAccess<CalendarEvent> events = evtDAO.findEventsByQuery(eventQuery);
1631
1632
1633 for (CalendarEvent event : events.load(offset, limit)) {
1634 data.add(buildEventResource(event, uri, expand, fields));
1635 }
1636 if (returnSize) {
1637 fullSize = events.getSize();
1638 }
1639 }
1640 } else {
1641 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1642 }
1643
1644 CollectionResource evData = new CollectionResource(data, fullSize);
1645 evData.setOffset(offset);
1646 evData.setLimit(limit);
1647
1648 ResponseBuilder response = buildJsonP(evData, jsonp);
1649
1650 if (returnSize) {
1651 response.header(HEADER_LINK, buildFullUrl(uri, offset, limit, fullSize));
1652 }
1653
1654
1655 return response.build();
1656 }
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753 @SuppressWarnings({ "rawtypes", "unchecked" })
1754 @GET
1755 @RolesAllowed("users")
1756 @Path("/events")
1757 @Produces(MediaType.APPLICATION_JSON)
1758 @ApiOperation(
1759 value = "Returns the events of a calendar identified by its ID",
1760 notes = "Returns events of an calendar with specified id when:<br/>"
1761 + "- the calendar is public<br/>"
1762 + "- the authenticated user is the owner of the calendar of the event<br/>"
1763 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
1764 + "- the authenticated user is a participant of the event<br/>"
1765 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user")
1766 @ApiResponses(value = {
1767 @ApiResponse(code = 200, message = "Successful retrieval of all events from the calendar"),
1768 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found")
1769 })
1770 public Response getEvents(
1771 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *from* this date", required = false, defaultValue = "Current server time") @QueryParam("startTime") String start,
1772 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *to* this date", required = false, defaultValue = "Current server time + 1 week") @QueryParam("endTime") String end,
1773 @ApiParam(value = "Search for this category only. If not specified, search event of any category", required = false) @QueryParam("category") String category,
1774 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
1775 @ApiParam(value = "The maximum number of results when paging through a list of entities. If not specified or exceed the *query_limit* configuration of calendar rest service, it will use the *query_limit*", required = false) @QueryParam("limit") int limit,
1776 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
1777 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
1778 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link", required = false) @QueryParam("expand") String expand,
1779 @ApiParam(value = "Tells the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
1780 @Context UriInfo uri) throws Exception {
1781 limit = parseLimit(limit);
1782 String username = currentUserId();
1783
1784 CalendarService service = calendarServiceInstance();
1785 EventDAO evtDAO = service.getEventDAO();
1786
1787 long fullSize = returnSize ? 0 : -1;
1788 List data = new LinkedList();
1789 List<Calendar> calendarList;
1790 try {
1791 calendarList = getCalendarsOfUser(service, username);
1792 } catch (Exception e) {
1793 log.error("Cannot find calendars of user " + username, e);
1794 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1795 }
1796
1797 EventQuery eventQuery = buildEventQuery(start, end, category, calendarList,
1798 null, username, CalendarEvent.TYPE_EVENT);
1799 ListAccess<CalendarEvent> events = evtDAO.findEventsByQuery(eventQuery);
1800
1801
1802 for (CalendarEvent event : events.load(offset, limit)) {
1803 data.add(buildEventResource(event, uri, expand, fields));
1804 }
1805 if (returnSize) {
1806 fullSize = events.getSize();
1807 }
1808 CollectionResource evData = new CollectionResource(data, fullSize);
1809 evData.setOffset(offset);
1810 evData.setLimit(limit);
1811
1812 ResponseBuilder response = buildJsonP(evData, jsonp);
1813
1814 if (returnSize) {
1815 response.header(HEADER_LINK, buildFullUrl(uri, offset, limit, fullSize));
1816 }
1817
1818
1819 return response.build();
1820 }
1821
1822 private List<Calendar> getCalendarsOfUser(CalendarService service, String username) throws Exception {
1823 List<Calendar> list = new ArrayList<>();
1824 List<GroupCalendarData> listgroupCalendar = service.getGroupCalendars(getUserGroups(username), true, username);
1825 for (GroupCalendarData group : listgroupCalendar) {
1826 Optional.ofNullable(group.getCalendars()).ifPresent(list::addAll);
1827 }
1828 Optional.ofNullable(service.getUserCalendars(username, true)).ifPresent(list::addAll);
1829 return list;
1830 }
1831
1832 private String[] getUserGroups(String username) throws Exception {
1833 String [] groupsList;
1834 Object[] objs = orgService.getGroupHandler().findGroupsOfUser(username).toArray();
1835 groupsList = new String[objs.length];
1836 for (int i = 0; i < objs.length; i++) {
1837 groupsList[i] = ((Group) objs[i]).getId();
1838 }
1839 return groupsList;
1840 }
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879 @POST
1880 @RolesAllowed("users")
1881 @Path("/calendars/{id}/events")
1882 @ApiOperation(
1883 value = "Creates an event in a Calendar identified by its ID",
1884 notes = "Creates an event in a calendar with specified id only if:<br/>"
1885 + "- the authenticated user is the owner of the calendar<br/>"
1886 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
1887 + "- the calendar has been shared with the authenticated user, with modification rights<br/>"
1888 + "- the calendar has been shared with a group of the authenticated user, with modification rights")
1889 @ApiResponses(value = {
1890 @ApiResponse(code = 201, message = "Event successfully created in the Calendar"),
1891 @ApiResponse(code = 400, message = "Bad Request: Provided attributes are not valid (not following the rules of evObject)"),
1892 @ApiResponse(code = 401, message = "User unauthorized to create an event in this calendar"),
1893 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
1894 @ApiResponse(code = 503, message = "An error occurred during the saving process")
1895 })
1896 public Response createEventForCalendar(
1897 @ApiParam(value = "Identity of the calendar where the event is created", required = true) @PathParam("id") String id,
1898 EventResource evObject,
1899 @Context UriInfo uriInfo) {
1900 try {
1901 Calendar cal = calendarServiceInstance().getCalendarById(id);
1902 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
1903 CalendarEvent newEvent = new CalendarEvent();
1904 if (evObject.getSubject() == null) {
1905 evObject.setSubject(DEFAULT_EVENT_NAME);
1906 }
1907 if (evObject.getCategoryId() == null) {
1908 evObject.setCategoryId(CalendarService.DEFAULT_EVENTCATEGORY_ID_ALL);
1909 }
1910 Response error = buildEvent(newEvent, evObject, null);
1911 if (error != null) {
1912 return error;
1913 }
1914 if (Utils.isCalendarEditable(currentUserId(), cal)) {
1915 int calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), id);
1916
1917 newEvent.setCalendarId(id);
1918 saveEvent(calType, newEvent, true);
1919 String username = ConversationState.getCurrent().getIdentity().getUserId();
1920 MailNotification mail = new MailNotification(mailService, orgService, calendarServiceInstance());
1921 mail.sendEmail(newEvent,username);
1922
1923 String location = new StringBuilder(getBasePath(uriInfo)).append(EVENT_URI).append(newEvent.getId()).toString();
1924 return Response.status(HTTPStatus.CREATED).header(HEADER_LOCATION, location).cacheControl(nc).build();
1925 } else {
1926 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
1927 }
1928 } catch (Exception e) {
1929 if(log.isDebugEnabled()) log.debug(e.getMessage());
1930 }
1931 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
1932
1933 }
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034 @SuppressWarnings({ "unchecked", "rawtypes" })
2035 @GET
2036 @RolesAllowed("users")
2037 @Path("/events/{id}/occurrences")
2038 @Produces(MediaType.APPLICATION_JSON)
2039 @ApiOperation(
2040 value = "Returns occurrences of a recurring event identified by its ID",
2041 notes = "Returns occurrences of a recurring event with specified id when :<br/>"
2042 + "- the calendar of the event is public<br/>"
2043 + "- the authenticated user is the owner of the calendar of the event<br/>"
2044 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
2045 + "- the authenticated user is a participant of the event<br/>"
2046 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user")
2047 @ApiResponses(value = {
2048 @ApiResponse(code = 200, message = "Successful retrieval of all occurrences of the event"),
2049 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
2050 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2051 })
2052 public Response getOccurrencesFromEvent(
2053 @ApiParam(value = "Identity of the recurrent event", required = true) @PathParam("id") String id,
2054 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
2055 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
2056 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *from* this date.", required = false, defaultValue = "current server time") @QueryParam("start") String start,
2057 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *to* this date.", required = false, defaultValue = "current server time + 1 week") @QueryParam("end") String end,
2058 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2059 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2060 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link. This is a list of comma-separated property's names", required = false) @QueryParam("expand") String expand,
2061 @ApiParam(value = "Tells the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
2062 @Context UriInfo uriInfo) {
2063 try {
2064 limit = parseLimit(limit);
2065 java.util.Calendar[] dates = parseDate(start, end);
2066
2067 CalendarEvent recurEvent = calendarServiceInstance().getEventById(id);
2068 if (recurEvent == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2069 TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
2070 String timeZone = tz.getID();
2071
2072 Map<String,CalendarEvent> occMap = calendarServiceInstance().getOccurrenceEvents(recurEvent, dates[0], dates[1], timeZone);
2073 if(occMap == null || occMap.isEmpty()) {
2074 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2075 }
2076
2077 Calendar cal = calendarServiceInstance().getCalendarById(recurEvent.getCalendarId());
2078 boolean inParticipant = false;
2079 if (recurEvent.getParticipant() != null) {
2080 String[] participant = recurEvent.getParticipant();
2081 Arrays.sort(participant);
2082 int i = Arrays.binarySearch(participant, currentUserId());
2083 if (i > -1) inParticipant = true;
2084 }
2085
2086 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId()) || inParticipant) {
2087 Collection data = new ArrayList();
2088 Iterator<CalendarEvent> evIter = occMap.values().iterator();
2089 Utils.skip(evIter, offset);
2090
2091 int counter =0;
2092 while (evIter.hasNext()) {
2093 data.add(buildEventResource(evIter.next(), uriInfo, expand, fields));
2094 if(++counter == limit) break;
2095 }
2096
2097 int fullSize = returnSize ? occMap.values().size() : -1;
2098 CollectionResource evData = new CollectionResource(data, fullSize);
2099 evData.setOffset(offset);
2100 evData.setLimit(limit);
2101
2102
2103 ResponseBuilder response = buildJsonP(evData, jsonp);
2104 if (returnSize) {
2105 response.header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, evData.getSize()));
2106 }
2107 return response.build();
2108 }
2109
2110
2111 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2112 } catch (Exception e) {
2113 if(log.isDebugEnabled()) log.debug(e.getMessage());
2114 }
2115 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2116 }
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215 @SuppressWarnings({ "rawtypes", "unchecked" })
2216 @GET
2217 @RolesAllowed("users")
2218 @Path("/calendars/{id}/tasks")
2219 @Produces(MediaType.APPLICATION_JSON)
2220 @ApiOperation(
2221 value = "Returns tasks of a calendar identified by its ID",
2222 notes = "Returns tasks of a calendar with specified id when:<br/>"
2223 + "- the calendar is public<br/>"
2224 + "- the authenticated user is the owner of the calendar of the task<br/>"
2225 + "- the authenticated user belongs to the group of the calendar of the task<br/>"
2226 + "- the authenticated user is delegated by the task<br/>"
2227 + "- the calendar of the task has been shared with the authenticated user or with a group of the authenticated user")
2228 @ApiResponses(value = {
2229 @ApiResponse(code = 200, message = "Successful retrieval of all tasks from the calendar"),
2230 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found")
2231 })
2232 public Response getTasksByCalendar(
2233 @ApiParam(value = "Identity of a calendar to search for tasks", required = true) @PathParam("id") String id,
2234 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *from* this date.", required = false, defaultValue = "current server time")@QueryParam("startTime") String start,
2235 @ApiParam(value = "Date follow ISO8601 (YYYY-MM-DDThh:mm:ssTZD). Search for events *to* this date.", required = false, defaultValue = "current server time + 1 week") @QueryParam("endTime") String end,
2236 @ApiParam(value = "Search for this category only", required = false, defaultValue = "If not specified, search task of any category") @QueryParam("category") String category,
2237 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
2238 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
2239 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2240 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2241 @ApiParam(value = "used to ask for a full representation of a subresource, instead of only its link", required = false) @QueryParam("expand") String expand,
2242 @ApiParam(value = "Tells the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
2243 @Context UriInfo uri) throws Exception {
2244 limit = parseLimit(limit);
2245 String username = currentUserId();
2246
2247 CalendarService service = calendarServiceInstance();
2248 EventDAO evtDAO = service.getEventDAO();
2249
2250 long fullSize = returnSize ? 0 : -1;
2251 List data = new LinkedList();
2252 Calendar calendar = service.getCalendarById(id);
2253
2254 if (calendar != null) {
2255 String participant = null;
2256 if (calendar.getPublicUrl() == null && !hasViewCalendarPermission(calendar, username)) {
2257 participant = username;
2258 }
2259
2260 EventQuery eventQuery = buildEventQuery(start, end, category, Arrays.asList(calendar),
2261 id, participant, CalendarEvent.TYPE_TASK);
2262 ListAccess<CalendarEvent> events = evtDAO.findEventsByQuery(eventQuery);
2263
2264
2265 for (CalendarEvent event : events.load(offset, limit)) {
2266 data.add(buildTaskResource(event, uri, expand, fields));
2267 }
2268 if (returnSize) {
2269 fullSize = events.getSize();
2270 }
2271 } else {
2272 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2273 }
2274
2275 CollectionResource evData = new CollectionResource(data, fullSize);
2276 evData.setOffset(offset);
2277 evData.setLimit(limit);
2278
2279 ResponseBuilder response = buildJsonP(evData, jsonp);
2280
2281 if (returnSize) {
2282 response.header(HEADER_LINK, buildFullUrl(uri, offset, limit, fullSize));
2283 }
2284
2285
2286 return response.build();
2287 }
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330 @POST
2331 @RolesAllowed("users")
2332 @Path("/calendars/{id}/tasks")
2333 @ApiOperation(
2334 value = "Creates a task for a calendar identified by its ID",
2335 notes = "Creates a task for a calendar with specified id only if:<br/>"
2336 + "- the authenticated user is the owner of the calendar<br/>"
2337 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
2338 + "- the calendar has been shared with the authenticated user, with modification rights<br/>"
2339 + "- the calendar has been shared with a group of the authenticated user, with modification rights<br/>")
2340 @ApiResponses(value = {
2341 @ApiResponse(code = 201, message = "Task successfully created"),
2342 @ApiResponse(code = 400, message = "Bad Request: Provided attributes are not valid (not following the rules of evObject)"),
2343 @ApiResponse(code = 401, message = "User unauthorized to create a task for this calendar"),
2344 @ApiResponse(code = 404, message = "Calendar with provided ID Not Found"),
2345 @ApiResponse(code = 503, message = "An error occurred during saving process")
2346 })
2347 public Response createTaskForCalendar(
2348 @ApiParam(value = "Identity of the calendar where the task is created", required = true) @PathParam("id") String id,
2349 TaskResource evObject,
2350 @Context UriInfo uriInfo) {
2351 try {
2352 Calendar cal = calendarServiceInstance().getCalendarById(id);
2353 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2354
2355 CalendarEvent newEvent = new CalendarEvent();
2356 newEvent.setEventType(CalendarEvent.TYPE_TASK);
2357 if (evObject.getName() == null) {
2358 evObject.setName(DEFAULT_EVENT_NAME);
2359 }
2360 if (evObject.getCategoryId() == null) {
2361 evObject.setCategoryId(CalendarService.DEFAULT_EVENTCATEGORY_ID_ALL);
2362 }
2363 Response error = buildEventFromTask(newEvent, evObject);
2364 if (error != null) {
2365 return error;
2366 }
2367 if (Utils.isCalendarEditable(currentUserId(), cal)) {
2368 int calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), id);
2369
2370 newEvent.setCalendarId(id);
2371 saveEvent(calType, newEvent, true);
2372
2373 String location = new StringBuilder(getBasePath(uriInfo)).append(TASK_URI).append(newEvent.getId()).toString();
2374 return Response.status(HTTPStatus.CREATED).header(HEADER_LOCATION, location).cacheControl(nc).build();
2375 } else {
2376 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
2377 }
2378 } catch (Exception e) {
2379 if(log.isDebugEnabled()) log.debug(e.getMessage());
2380 }
2381 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2382
2383 }
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460 @GET
2461 @RolesAllowed("users")
2462 @Path("/tasks/{id}")
2463 @Produces(MediaType.APPLICATION_JSON)
2464 @ApiOperation(
2465 value = "Returns a task identified by its ID",
2466 notes = "Returns a task with specified id if:<br/>"
2467 + "- the calendar of the task is public<br/>"
2468 + "- the authenticated user is the owner of the calendar of the task<br/>"
2469 + "- the authenticated user belongs to the group of the calendar of the task<br/>"
2470 + "- the authenticated user is a participant of the task<br/>"
2471 + "- the calendar of the task has been shared with the authenticated user or with a group of the authenticated user"
2472 )
2473 @ApiResponses(value = {
2474 @ApiResponse(code = 200, message = "Successful retrieval of the task"),
2475 @ApiResponse(code = 404, message = "Task with provided ID Not Found"),
2476 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2477 })
2478 public Response getTaskById(
2479 @ApiParam(value = "Identity of the task to find", required = true) @PathParam("id") String id,
2480 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2481 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link. This is a list of comma-separated property's names", required = false) @QueryParam("expand") String expand,
2482 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2483 @Context UriInfo uriInfo,
2484 @Context Request request) {
2485 try {
2486 CalendarEvent ev = calendarServiceInstance().getEventById(id);
2487 if(ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2488
2489 Date lastModified = new Date(ev.getLastModified());
2490 ResponseBuilder preCondition = request.evaluatePreconditions(lastModified);
2491 if (preCondition != null) {
2492 return preCondition.build();
2493 }
2494
2495 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
2496 boolean inParticipant = false;
2497 if (ev.getParticipant() != null) {
2498 String[] participant = ev.getParticipant();
2499 Arrays.sort(participant);
2500 if (Arrays.binarySearch(participant, currentUserId()) > -1) inParticipant = true;;
2501 }
2502
2503 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId()) || inParticipant) {
2504 Object resource = buildTaskResource(ev, uriInfo, expand, fields);
2505 return buildJsonP(resource, jsonp).cacheControl(cc).lastModified(lastModified).build();
2506 } else {
2507 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2508 }
2509 } catch (Exception e) {
2510 if(log.isDebugEnabled()) log.debug(e.getMessage());
2511 }
2512 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2513 }
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561 @PUT
2562 @RolesAllowed("users")
2563 @Path("/tasks/{id}")
2564 @ApiOperation(
2565 value = "Updates a task identified by its ID",
2566 notes = "Updates a task with the specified id if:<br/>"
2567 + "- the authenticated user is the owner of the calendar of the event<br/>"
2568 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
2569 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
2570 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
2571 @ApiResponses(value = {
2572 @ApiResponse(code = 200, message = "Task successfully updated"),
2573 @ApiResponse(code = 400, message = "Bad Request: Provided attributes are not valid (not following the rules of evObject)"),
2574 @ApiResponse(code = 401, message = "User unauthorized to update this task"),
2575 @ApiResponse(code = 404, message = "Task with provided ID Not Found"),
2576 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2577 })
2578 public Response updateTaskById(
2579 @ApiParam(value = "Identity of the task to update", required = true) @PathParam("id") String id,
2580 TaskResource evObject) {
2581 try {
2582 CalendarEvent event = calendarServiceInstance().getEventById(id);
2583 if (event == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2584 Calendar cal = calendarServiceInstance().getCalendarById(event.getCalendarId());
2585 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2586
2587 if (Utils.isCalendarEditable(currentUserId(), cal)) {
2588 int calType = -1;
2589 try {
2590 calType = Integer.parseInt(event.getCalType());
2591 }catch (NumberFormatException e) {
2592 calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), event.getCalendarId());
2593 }
2594 buildEventFromTask(event, evObject);
2595
2596 saveEvent(calType, event, false);
2597
2598 return Response.ok().cacheControl(nc).build();
2599 } else {
2600 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
2601 }
2602 } catch (Exception e) {
2603 if(log.isDebugEnabled()) log.debug(e.getMessage());
2604 }
2605 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2606 }
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630 @DELETE
2631 @RolesAllowed("users")
2632 @Path("/tasks/{id}")
2633 @ApiOperation(
2634 value = "Deletes a task identified by its ID",
2635 notes = "Deletes a task with specified id if:<br/>"
2636 + "- the authenticated user is the owner of the calendar of the event<br/>"
2637 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
2638 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
2639 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
2640 @ApiResponses(value = {
2641 @ApiResponse(code = 200, message = "Task successfully deleted"),
2642 @ApiResponse(code = 401, message = "User unauthorized to delete this task"),
2643 @ApiResponse(code = 404, message = "Task with provided ID Not Found"),
2644 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2645 })
2646 public Response deleteTaskById(
2647 @ApiParam(value = "Identity of the task to delete", required = true) @PathParam("id") String id) {
2648 try {
2649 CalendarEvent ev = calendarServiceInstance().getEventById(id);
2650 if (ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2651 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
2652 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2653
2654 if (Utils.isCalendarEditable(currentUserId(), cal)) {
2655 int calType = Calendar.TYPE_ALL;
2656 try {
2657 calType = Integer.parseInt(ev.getCalType());
2658 } catch (NumberFormatException e) {
2659 calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), ev.getCalendarId());
2660 }
2661 switch (calType) {
2662 case Calendar.TYPE_PRIVATE:
2663 calendarServiceInstance().removeUserEvent(currentUserId(), ev.getCalendarId(), id);
2664 break;
2665 case Calendar.TYPE_PUBLIC:
2666 calendarServiceInstance().removePublicEvent(ev.getCalendarId(),id);
2667 break;
2668 case Calendar.TYPE_SHARED:
2669 calendarServiceInstance().removeSharedEvent(currentUserId(), ev.getCalendarId(), id);
2670 break;
2671
2672 default:
2673 break;
2674 }
2675 return Response.ok().cacheControl(nc).build();
2676 } else {
2677 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
2678 }
2679 } catch (Exception e) {
2680 if(log.isDebugEnabled()) log.debug(e.getMessage());
2681 }
2682 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2683 }
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719 @GET
2720 @RolesAllowed("users")
2721 @Path("/attachments/{id}")
2722 @Produces(MediaType.APPLICATION_JSON)
2723 @ApiOperation(
2724 value = "Returns an attachment identified by its ID",
2725 notes = "Returns an attachment with specified id if:<br/>"
2726 + "- the calendar of the event is public<br/>"
2727 + "- the authenticated user is the owner of the calendar of the event<br/>"
2728 + "- the authenticated user belongs to the group of the calendar of the event<br/>"
2729 + "- the authenticated user is a participant of the event<br/>"
2730 + "- the calendar of the event has been shared with the authenticated user or with a group of the authenticated user")
2731 @ApiResponses(value = {
2732 @ApiResponse(code = 200, message = "Successful retrieval of the attachment"),
2733 @ApiResponse(code = 404, message = "Attachment with provided ID Not Found"),
2734 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2735 })
2736 public Response getAttachmentById(
2737 @ApiParam(value = "Identity of the attachment to find", required = true) @PathParam("id") String id,
2738 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2739 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2740 @Context UriInfo uriInfo,
2741 @Context Request request) {
2742 try {
2743 id = AttachmentResource.decode(id);
2744 CalendarEvent ev = this.findEventAttachment(id);
2745 if (ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2746 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
2747 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2748 Attachment att = calendarServiceInstance().getAttachmentById(id);
2749 if(att == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2750
2751 Date lastModified = new Date(att.getLastModified());
2752 ResponseBuilder preCondition = request.evaluatePreconditions(lastModified);
2753 if (preCondition != null) {
2754 return preCondition.build();
2755 }
2756
2757 boolean inParticipant = false;
2758 if (ev.getParticipant() != null) {
2759 String[] participant = ev.getParticipant();
2760 Arrays.sort(participant);
2761 int i = Arrays.binarySearch(participant, currentUserId());
2762 if (i > -1) inParticipant = true;
2763 }
2764
2765 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, currentUserId()) || inParticipant) {
2766
2767 AttachmentResource evData = new AttachmentResource(att, getBasePath(uriInfo));
2768 Object resource = extractObject(evData, fields);
2769 if (jsonp != null) {
2770 String json = null;
2771 if (resource instanceof Map) json = new JSONObject(resource).toString();
2772 else {
2773 JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl();
2774 json = generatorImpl.createJsonObject(resource).toString();
2775 }
2776 StringBuilder sb = new StringBuilder(jsonp);
2777 sb.append("(").append(json).append(");");
2778 return Response.ok(sb.toString(), new MediaType("text", "javascript")).cacheControl(cc).lastModified(lastModified).build();
2779 }
2780
2781
2782 return Response.ok(resource, MediaType.APPLICATION_JSON).cacheControl(cc).lastModified(lastModified).build();
2783 }
2784
2785
2786 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2787 } catch (Exception e) {
2788 if(log.isDebugEnabled()) log.debug(e.getMessage());
2789 }
2790 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2791 }
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815 @DELETE
2816 @RolesAllowed("users")
2817 @Path("/attachments/{id}")
2818 @ApiOperation(
2819 value = "Deletes an attachment identified by its ID",
2820 notes = "Deletes an attachment with specified id if:<br/>"
2821 + "- the authenticated user is the owner of the calendar of the event<br/>"
2822 + "- for group calendars, the authenticated user has edit rights on the calendar<br/>"
2823 + "- the calendar of the event has been shared with the authenticated user, with modification rights<br/>"
2824 + "- the calendar of the event has been shared with a group of the authenticated user, with modification rights")
2825 @ApiResponses(value = {
2826 @ApiResponse(code = 200, message = "Attachment successfully deleted"),
2827 @ApiResponse(code = 401, message = "User unauthorized to delete this attachment"),
2828 @ApiResponse(code = 404, message = "Attachment with provided ID Not Found"),
2829 @ApiResponse(code = 503, message = "An error occured during the saving process")
2830 })
2831 public Response deleteAttachmentById(
2832 @ApiParam(value = "Identity of the attachment to delete", required = true) @PathParam("id") String id) {
2833 try {
2834 id = AttachmentResource.decode(id);
2835 CalendarEvent ev = this.findEventAttachment(id);
2836 if (ev == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2837 Calendar cal = calendarServiceInstance().getCalendarById(ev.getCalendarId());
2838 if (cal == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2839
2840 if (Utils.isCalendarEditable(currentUserId(), cal)) {
2841 calendarServiceInstance().removeAttachmentById(id);
2842 return Response.ok().cacheControl(nc).build();
2843 }
2844 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
2845 } catch (Exception e) {
2846 if(log.isDebugEnabled()) log.debug(e.getMessage());
2847 }
2848 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2849 }
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889 @SuppressWarnings({ "rawtypes", "unchecked" })
2890 @GET
2891 @RolesAllowed("users")
2892 @Path("/categories")
2893 @Produces(MediaType.APPLICATION_JSON)
2894 @ApiOperation(
2895 value = "Returns the categories accessible to the user",
2896 notes = "Returns the categories if a user is authenticated (the common categories + the personal categories)")
2897 @ApiResponses(value = {
2898 @ApiResponse(code = 200, message = "Successful retrieval of all event categories"),
2899 @ApiResponse(code = 404, message = "No categories Found"),
2900 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2901 })
2902 public Response getEventCategories(
2903 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
2904 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
2905 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2906 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2907 @Context UriInfo uriInfo) {
2908 limit = parseLimit(limit);
2909
2910 try {
2911 List<EventCategory> ecData = calendarServiceInstance().getEventCategories(currentUserId(), offset, limit);
2912 if(ecData == null || ecData.isEmpty()) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2913 Collection data = new ArrayList();
2914
2915 String basePath = getBasePath(uriInfo);
2916 for(EventCategory ec:ecData) {
2917 data.add(extractObject(new CategoryResource(ec, basePath), fields));
2918 }
2919
2920 CollectionResource resource = new CollectionResource(data, ecData.size());
2921 resource.setOffset(offset);
2922 resource.setLimit(limit);
2923
2924 if (jsonp != null) {
2925 JsonValue json = new JsonGeneratorImpl().createJsonObject(resource);
2926 StringBuilder sb = new StringBuilder(jsonp);
2927 sb.append("(").append(json).append(");");
2928 return Response.ok(sb.toString(), new MediaType("text", "javascript")).header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, resource.getSize())).cacheControl(nc).build();
2929 }
2930
2931
2932 return Response.ok(resource, MediaType.APPLICATION_JSON).header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, resource.getSize())).cacheControl(nc).build();
2933 } catch (Exception e) {
2934 if(log.isDebugEnabled()) log.debug(e.getMessage());
2935 }
2936 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
2937 }
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966 @GET
2967 @RolesAllowed("users")
2968 @Path("/categories/{id}")
2969 @Produces(MediaType.APPLICATION_JSON)
2970 @ApiOperation(
2971 value = "Returns an event category identified by its ID",
2972 notes = "Returns the event category by id if it belongs to the user")
2973 @ApiResponses(value = {
2974 @ApiResponse(code = 200, message = "Successful retrieval of the event category"),
2975 @ApiResponse(code = 404, message = "Event category with provided ID Not Found"),
2976 @ApiResponse(code = 503, message = "An error occurred during the saving process")
2977 })
2978 public Response getEventCategoryById(
2979 @ApiParam(value = "Identity of the event category to find", required = true) @PathParam("id") String id,
2980 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
2981 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
2982 @Context UriInfo uriInfo,
2983 @Context Request request) {
2984 try {
2985 List<EventCategory> data = calendarServiceInstance().getEventCategories(currentUserId());
2986 if(data == null || data.isEmpty()) {
2987 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2988 }
2989 EventCategory category = null;
2990 for (int i = 0; i < data.size(); i++) {
2991 if(id.equals(data.get(i).getId())) {
2992 category = data.get(i);
2993 break;
2994 }
2995 }
2996
2997 if(category == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
2998
2999 Date lastModified = new Date(category.getLastModified());
3000 ResponseBuilder preCondition = request.evaluatePreconditions(lastModified);
3001 if (preCondition != null) {
3002 return preCondition.build();
3003 }
3004
3005 CategoryResource categoryR = new CategoryResource(category, getBasePath(uriInfo));
3006 Object resource = extractObject(categoryR, fields);
3007 if (jsonp != null) {
3008 String json = null;
3009 if (resource instanceof Map) json = new JSONObject((Map<?, ?>)resource).toString();
3010 else {
3011 JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl();
3012 json = generatorImpl.createJsonObject(resource).toString();
3013 }
3014 StringBuilder sb = new StringBuilder(jsonp);
3015 sb.append("(").append(json).append(");");
3016 return Response.ok(sb.toString(), new MediaType("text", "javascript")).cacheControl(cc).lastModified(lastModified).build();
3017 }
3018
3019
3020 return Response.ok(resource, MediaType.APPLICATION_JSON).cacheControl(cc).lastModified(lastModified).build();
3021 } catch (Exception e) {
3022 if(log.isDebugEnabled()) log.debug(e.getMessage());
3023 }
3024 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3025 }
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083 @GET
3084 @RolesAllowed("users")
3085 @Path("/feeds/{id}")
3086 @Produces(MediaType.APPLICATION_JSON)
3087 @ApiOperation(
3088 value = "Returns a feed identified by its ID",
3089 notes = "Returns the feed with the given ID if the authenticated user is the owner of the feed")
3090 @ApiResponses(value = {
3091 @ApiResponse(code = 200, message = "Successful retrieval of the feed"),
3092 @ApiResponse(code = 404, message = "Feed with provided ID Not Found"),
3093 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3094 })
3095 public Response getFeedById(
3096 @ApiParam(value = "Title of the feed to find", required = true) @PathParam("id") String id,
3097 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
3098 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link. This is a list of comma-separated property's names", required = false) @QueryParam("expand") String expand,
3099 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
3100 @Context UriInfo uriInfo,
3101 @Context Request request) {
3102 try {
3103 FeedData feed = null;
3104 for (FeedData feedData : calendarServiceInstance().getFeeds(currentUserId())) {
3105 if (feedData.getTitle().equals(id)) {
3106 feed = feedData;
3107 break;
3108 }
3109 }
3110 if(feed == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3111 byte[] data = feed.getContent();
3112
3113 byte[] hashCode = digest(data).getBytes();
3114 EntityTag tag = new EntityTag(new String(hashCode));
3115 ResponseBuilder preCondition = request.evaluatePreconditions(tag);
3116 if (preCondition != null) {
3117 return preCondition.build();
3118 }
3119
3120 SyndFeedInput input = new SyndFeedInput();
3121 SyndFeed syndFeed = input.build(new XmlReader(new ByteArrayInputStream(data)));
3122 List<SyndEntry> entries = new ArrayList<SyndEntry>(syndFeed.getEntries());
3123 List<String> calIds = new ArrayList<String>();
3124 for (SyndEntry entry : entries) {
3125 String calendarId = entry.getLink().substring(entry.getLink().lastIndexOf("/")+1) ;
3126 calIds.add(calendarId);
3127 }
3128
3129 Object resource = buildFeedResource(feed, calIds, uriInfo, expand, fields);
3130 return buildJsonP(resource, jsonp).cacheControl(cc).tag(tag).build();
3131 } catch (Exception e) {
3132 if(log.isDebugEnabled()) log.debug(e.getMessage());
3133 }
3134 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3135 }
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164 @PUT
3165 @RolesAllowed("users")
3166 @Path("/feeds/{id}")
3167 @ApiOperation(
3168 value = "Updates a feed identified by its ID",
3169 notes = "Updates the feed with the given ID if the authenticated user is the owner of the feed")
3170 @ApiResponses(value = {
3171 @ApiResponse(code = 200, message = "Feed successfully updated"),
3172 @ApiResponse(code = 404, message = "Feed with provided ID Not Found"),
3173 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3174 })
3175 public Response updateFeedById(
3176 @ApiParam(value = "Title of the feed to update", required = true) @PathParam("id") String id,
3177 FeedResource feedResource) {
3178 try {
3179 FeedData feed = null;
3180 for (FeedData feedData : calendarServiceInstance().getFeeds(currentUserId())) {
3181 if (feedData.getTitle().equals(id)) {
3182 feed = feedData;
3183 break;
3184 }
3185 }
3186
3187 if (feed == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3188
3189 LinkedHashMap<String, Calendar> calendars = new LinkedHashMap<String, Calendar>();
3190 if (feedResource.getCalendarIds() != null) {
3191 for (String calendarId : feedResource.getCalendarIds()) {
3192 Calendar calendar = calendarServiceInstance().getCalendarById(calendarId);
3193 int calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), calendarId);
3194 switch (calType) {
3195 case Calendar.TYPE_PRIVATE:
3196 calendars.put(Calendar.TYPE_PRIVATE + Utils.COLON + calendarId, calendar);
3197 break;
3198 case Calendar.TYPE_PUBLIC:
3199 calendars.put(Calendar.TYPE_PUBLIC + Utils.COLON + calendarId, calendar);
3200 break;
3201 case Calendar.TYPE_SHARED:
3202 calendars.put(Calendar.TYPE_SHARED + Utils.COLON + calendarId, calendar);
3203 break;
3204 default:
3205 break;
3206 }
3207 }
3208 }
3209
3210
3211 calendarServiceInstance().removeFeedData(currentUserId(),id);
3212
3213 RssData rssData = new RssData();
3214 if (feedResource.getName() != null) {
3215 rssData.setName(feedResource.getName() + Utils.RSS_EXT) ;
3216 rssData.setTitle(feedResource.getName()) ;
3217 rssData.setDescription(feedResource.getName());
3218 }
3219 rssData.setUrl(feed.getUrl()) ;
3220 rssData.setLink(feed.getUrl());
3221 rssData.setVersion("rss_2.0") ;
3222
3223 calendarServiceInstance().generateRss(currentUserId(), calendars, rssData);
3224
3225 return Response.ok().cacheControl(nc).build();
3226 } catch (Exception e) {
3227 if(log.isDebugEnabled()) log.debug(e.getMessage());
3228 }
3229 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3230 }
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248 @DELETE
3249 @RolesAllowed("users")
3250 @Path("/feeds/{id}")
3251 @ApiOperation(
3252 value = "Deletes a feed identified by its ID",
3253 notes = "Deletes the feed with the given ID if the authenticated user is the owner of the feed")
3254 @ApiResponses(value = {
3255 @ApiResponse(code = 200, message = "Feed successfully deleted"),
3256 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3257 })
3258 public Response deleteFeedById(
3259 @ApiParam(value = "Title of the feed to delete", required = true) @PathParam("id") String id) {
3260 try {
3261 calendarServiceInstance().removeFeedData(currentUserId(),id);
3262 return Response.ok().cacheControl(nc).build();
3263 } catch (Exception e) {
3264 if(log.isDebugEnabled()) log.debug(e.getMessage());
3265 }
3266 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3267 }
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286 @GET
3287 @RolesAllowed("users")
3288 @Path("/feeds/{id}/rss")
3289 @Produces(MediaType.APPLICATION_XML)
3290 @ApiOperation(
3291 value = "Gets the RSS stream of the feed with the given ID",
3292 notes = "Returns the RSS stream if:<br/>"
3293 + "- the calendar is public<br/>"
3294 + "- the authenticated user is the owner of the calendar<br/>"
3295 + "- the authenticated user belongs to the group of the calendar<br/>"
3296 + "- the calendar has been shared with the authenticated user or with a group of the authenticated user")
3297 @ApiResponses(value = {
3298 @ApiResponse(code = 200, message = "Successful retrieval of RSS stream from the feed"),
3299 @ApiResponse(code = 404, message = "Feed with provided ID Not Found"),
3300 @ApiResponse(code = 503, message = "An error occurrred during the saving process")
3301 })
3302 public Response getRssFromFeed(
3303 @ApiParam(value = "Title of the feed", required = true) @PathParam("id") String id,
3304 @Context UriInfo uri,
3305 @Context Request request) {
3306 try {
3307 String username = currentUserId();
3308 String feedname = id;
3309 FeedData feed = null;
3310 for (FeedData feedData : calendarServiceInstance().getFeeds(username)) {
3311 if (feedData.getTitle().equals(feedname)) {
3312 feed = feedData;
3313 break;
3314 }
3315 }
3316
3317 if (feed == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3318
3319 SyndFeedInput input = new SyndFeedInput();
3320 SyndFeed syndFeed = input.build(new XmlReader(new ByteArrayInputStream(feed.getContent())));
3321 List<SyndEntry> entries = new ArrayList<SyndEntry>(syndFeed.getEntries());
3322 List<CalendarEvent> events = new ArrayList<CalendarEvent>();
3323 List<Calendar> calendars = new ArrayList<Calendar>();
3324 for (SyndEntry entry : entries) {
3325 String calendarId = entry.getLink().substring(entry.getLink().lastIndexOf("/")+1) ;
3326 calendars.add(calendarServiceInstance().getCalendarById(calendarId));
3327 }
3328
3329 for (Calendar cal : calendars) {
3330 if (cal.getPublicUrl() != null || this.hasViewCalendarPermission(cal, username)) {
3331 int calType = calendarServiceInstance().getTypeOfCalendar(username, cal.getId());
3332 switch (calType) {
3333 case Calendar.TYPE_PRIVATE:
3334 events.addAll(calendarServiceInstance().getUserEventByCalendar(username, Arrays.asList(cal.getId())));
3335 break;
3336 case Calendar.TYPE_SHARED:
3337 events.addAll(calendarServiceInstance().getSharedEventByCalendars(username, Arrays.asList(cal.getId())));
3338 break;
3339 case Calendar.TYPE_PUBLIC:
3340 EventQuery eventQuery = new EventQuery();
3341 eventQuery.setCalendarId(new String[] { cal.getId() });
3342 events.addAll(calendarServiceInstance().getPublicEvents(eventQuery));
3343 break;
3344 default:
3345 break;
3346 }
3347 }
3348 }
3349
3350 if(events.size() == 0) {
3351 return Response.status(HTTPStatus.NOT_FOUND).entity("Feed " + feedname + "is removed").cacheControl(nc).build();
3352 }
3353 String xml = makeFeed(username, events, feed, uri);
3354
3355 byte[] hashCode = digest(xml.getBytes()).getBytes();
3356 EntityTag tag = new EntityTag(new String(hashCode));
3357 ResponseBuilder preCondition = request.evaluatePreconditions(tag);
3358 if (preCondition != null) {
3359 return preCondition.build();
3360 }
3361
3362 return Response.ok(xml, MediaType.APPLICATION_XML).cacheControl(cc).tag(tag).build();
3363 } catch (Exception e) {
3364 if(log.isDebugEnabled()) log.debug(e.getMessage());
3365 }
3366 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3367 }
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402 @GET
3403 @RolesAllowed("users")
3404 @Path("/invitations/{id}")
3405 @Produces(MediaType.APPLICATION_JSON)
3406 @ApiOperation(
3407 value = "Returns an invitation identified by its ID",
3408 notes = "Returns an invitation with specified id if:<br/>"
3409 + "- the authenticated user is the participant of the invitation<br/>"
3410 + "- the authenticated user has edit rights on the calendar of the event of the invitation")
3411 @ApiResponses(value = {
3412 @ApiResponse(code = 200, message = "Successful retrieval of the invitation"),
3413 @ApiResponse(code = 404, message = "Invitation with provided ID Not Found")
3414 })
3415 public Response getInvitationById(
3416 @ApiParam(value = "Identity of the invitation to find", required = true) @PathParam("id") String id,
3417 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
3418 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
3419 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link. This is a list of comma-separated property's names", required = false) @QueryParam("expand") String expand,
3420 @Context UriInfo uriInfo,
3421 @Context Request request) throws Exception {
3422 CalendarService service = calendarServiceInstance();
3423 EventDAO evtDAO = service.getEventDAO();
3424 String username = currentUserId();
3425
3426 Invitation invitation = evtDAO.getInvitationById(id);
3427 if (invitation == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3428
3429 EntityTag tag = new EntityTag(String.valueOf(invitation.hashCode()));
3430 ResponseBuilder preCondition = request.evaluatePreconditions(tag);
3431 if (preCondition != null) {
3432 return preCondition.build();
3433 }
3434
3435
3436 if (!username.equals(invitation.getParticipant())) {
3437 CalendarEvent event = service.getEventById(invitation.getEventId());
3438 Calendar calendar = service.getCalendarById(event.getCalendarId());
3439
3440 if (!Utils.isCalendarEditable(username, calendar)) {
3441 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3442 }
3443 }
3444
3445 Object resource = buildInvitationResource(invitation, uriInfo, expand, fields);
3446 return buildJsonP(resource, jsonp).cacheControl(cc).tag(tag).build();
3447 }
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468 @PUT
3469 @RolesAllowed("users")
3470 @Path("/invitations/{id}")
3471 @ApiOperation(
3472 value = "Updates an invitation identified by its ID",
3473 notes = "Update the invitation if the authenticated user is the participant of the invitation.<br/>"
3474 + "This entry point only allow http PUT request, with id of invitation in the path, and the status")
3475 @ApiResponses(value = {
3476 @ApiResponse(code = 200, message = "Invitation successfully updated"),
3477 @ApiResponse(code = 400, message = "Bad Request: Status invalid"),
3478 @ApiResponse(code = 401, message = "User unauthorized to update the invitation"),
3479 @ApiResponse(code = 404, message = "Invitation with provided ID Not Found"),
3480 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3481 })
3482 public Response updateInvitationById(
3483 @ApiParam(value = "Identity of the invitation to update", required = true) @PathParam("id") String id,
3484 @ApiParam(value = "New status to update", allowableValues = "['', 'maybe', 'yes', 'no']", required = true) @QueryParam("status") String status) {
3485 if (Arrays.binarySearch(INVITATION_STATUS, status) < 0) {
3486 return buildBadResponse(new ErrorResource("status must be one of: " + StringUtils.join(INVITATION_STATUS, ","), "status"));
3487 }
3488 CalendarService service = calendarServiceInstance();
3489 EventDAO evtDAO = service.getEventDAO();
3490 String username = currentUserId();
3491
3492 Invitation invitation = evtDAO.getInvitationById(id);
3493 if (invitation != null) {
3494
3495 if (invitation.getParticipant().equals(username)) {
3496 evtDAO.updateInvitation(id, status);
3497 return Response.ok().cacheControl(nc).build();
3498 } else {
3499 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
3500 }
3501 } else {
3502 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3503 }
3504 }
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523 @DELETE
3524 @RolesAllowed("users")
3525 @Path("/invitations/{id}")
3526 @ApiOperation(
3527 value = "Deletes an invitation identified by its ID",
3528 notes = "Deletes an invitation with specified id if the authenticated user has edit rights on the calendar of the event of the invitation")
3529 @ApiResponses(value = {
3530 @ApiResponse(code = 200, message = "Invitation deleted successfully"),
3531 @ApiResponse(code = 401, message = "User unauthorized to delete this invitation"),
3532 @ApiResponse(code = 404, message = "Invitation with provided ID Not Found"),
3533 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3534 })
3535 public Response deleteInvitationById(
3536 @ApiParam(value = "Identity of the invitation to delete", required = true) @PathParam("id") String id) throws Exception {
3537 CalendarService calService = calendarServiceInstance();
3538 EventDAO evtDAO = calService.getEventDAO();
3539 String username = currentUserId();
3540
3541 Invitation invitation = evtDAO.getInvitationById(id);
3542 if (invitation == null) return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3543
3544 CalendarEvent event = calService.getEventById(invitation.getEventId());
3545 Calendar calendar = calService.getCalendarById(event.getCalendarId());
3546
3547 if (Utils.isCalendarEditable(username, calendar)) {
3548 evtDAO.removeInvitation(id);
3549 return Response.ok().cacheControl(nc).build();
3550 } else {
3551 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
3552 }
3553 }
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612 @SuppressWarnings({ "rawtypes", "unchecked" })
3613 @GET
3614 @RolesAllowed("users")
3615 @Path("/events/{id}/invitations/")
3616 @Produces(MediaType.APPLICATION_JSON)
3617 @ApiOperation(
3618 value = "Returns the invitations of an event identified by its ID",
3619 notes = "Returns invitations of an event with specified id when:<br/>"
3620 + "- the authenticated user is the participant of the invitation<br/>"
3621 + "- the authenticated user has edit rights on the calendar of the event of the invitation")
3622 @ApiResponses(value = {
3623 @ApiResponse(code = 200, message = "Successful retrieval of all invitations from the event"),
3624 @ApiResponse(code = 404, message = "Event with provided ID Not Found")
3625 })
3626 public Response getInvitationsFromEvent(
3627 @ApiParam(value = "Identity of the event to search for invitations", required = true) @PathParam("id") String id,
3628 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
3629 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
3630 @ApiParam(value = "Tells the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
3631 @ApiParam(value = "search for this status only", required = false, defaultValue = "If not specified, search invitation of any status ('', 'maybe', 'yes', 'no')") @QueryParam("status") String status,
3632 @ApiParam(value = "This is a list of comma separated property's names of response json object", required = false) @QueryParam("fields") String fields,
3633 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
3634 @ApiParam(value = "Used to ask for a full representation of a subresource, instead of only its link. This is a list of comma-separated property's names", required = false) @QueryParam("expand") String expand,
3635 @Context UriInfo uriInfo) throws Exception {
3636 limit = parseLimit(limit);
3637 CalendarService calService = calendarServiceInstance();
3638
3639 CalendarEvent event = calService.getEventById(id);
3640 String username = currentUserId();
3641
3642 List<Invitation> invitations = Collections.<Invitation>emptyList();
3643 if (event != null) {
3644
3645 invitations = new LinkedList<Invitation>(Arrays.asList(event.getInvitations()));
3646
3647
3648 Calendar calendar = calService.getCalendarById(event.getCalendarId());
3649 if (!Utils.isCalendarEditable(username, calendar)) {
3650 Iterator<Invitation> iter = invitations.iterator();
3651
3652 while(iter.hasNext()) {
3653 if (!iter.next().getParticipant().equals(username)) {
3654 iter.remove();
3655 }
3656 }
3657 }
3658
3659
3660 if (status != null) {
3661 Iterator<Invitation> iter = invitations.iterator();
3662 while(iter.hasNext()) {
3663 if (!iter.next().getStatus().equals(status)) {
3664 iter.remove();
3665 }
3666 }
3667 }
3668 }
3669
3670 List data = new LinkedList();
3671 for (Invitation invitation : Utils.subList(invitations, offset, limit)) {
3672 data.add(buildInvitationResource(invitation, uriInfo, expand, fields));
3673 }
3674 int fullSize = invitations.size();
3675
3676 CollectionResource ivData = new CollectionResource(data, returnSize ? fullSize : -1);
3677 ivData.setOffset(offset);
3678 ivData.setLimit(limit);
3679
3680 ResponseBuilder response = buildJsonP(ivData, jsonp);
3681 if (returnSize) {
3682 response.header(HEADER_LINK, buildFullUrl(uriInfo, offset, limit, fullSize));
3683 }
3684 return response.build();
3685 }
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711 @POST
3712 @RolesAllowed("users")
3713 @Path("/events/{id}/invitations/")
3714 @ApiOperation(
3715 value = "Creates an invitation in the event with the given id",
3716 notes = "Creates the invitation only if:<br/>"
3717 + "- the authenticated user is the participant of the invitation<br/>"
3718 + "- the authenticated user has edit rights on the calendar of the event of the invitation")
3719 @ApiResponses(value = {
3720 @ApiResponse(code = 201, message = "Invitation created successfully"),
3721 @ApiResponse(code = 400, message = "Bad Request: invalid participant or status"),
3722 @ApiResponse(code = 401, message = "User unauthorized to create an invitation for this event"),
3723 @ApiResponse(code = 404, message = "Event with provided ID Not Found"),
3724 @ApiResponse(code = 503, message = "An error occurred during the saving process")
3725 })
3726 public Response createInvitationForEvent(
3727 @ApiParam(value = "Identity of the event where the invitation is created", required = true) @PathParam("id") String id,
3728 InvitationResource invitation,
3729 @Context UriInfo uriInfo) throws Exception {
3730 if(invitation == null) {
3731 return buildBadResponse(new ErrorResource("Invitation information must not null", "invitation"));
3732 }
3733 String participant = invitation.getParticipant();
3734 String status = invitation.getStatus();
3735 if (participant == null || participant.trim().isEmpty()) {
3736 return buildBadResponse(new ErrorResource("participant must not null or empty", "participant"));
3737 }
3738 if (Arrays.binarySearch(INVITATION_STATUS, status) < 0) {
3739 return buildBadResponse(new ErrorResource("status must be one of: " + StringUtils.join(INVITATION_STATUS, ","), "status"));
3740 }
3741
3742 CalendarService service = calendarServiceInstance();
3743 EventDAO evtDAO = service.getEventDAO();
3744 String username = currentUserId();
3745
3746 CalendarEvent event = service.getEventById(id);
3747 if (event != null) {
3748 Calendar calendar = service.getCalendarById(event.getCalendarId());
3749 if (!Utils.isCalendarEditable(username, calendar)) {
3750 return Response.status(HTTPStatus.UNAUTHORIZED).cacheControl(nc).build();
3751 }
3752
3753 Invitation invite = evtDAO.createInvitation(id, participant, status);
3754 if (invite != null) {
3755 String location = new StringBuilder(getBasePath(uriInfo)).append(INVITATION_URI).append(invite.getId()).toString();
3756 return Response.status(HTTPStatus.CREATED).header(HEADER_LOCATION, location).cacheControl(nc).build();
3757 } else {
3758 return buildBadResponse(new ErrorResource(participant + " has already been participant, can't create more", "participant"));
3759 }
3760 } else {
3761 return Response.status(HTTPStatus.NOT_FOUND).cacheControl(nc).build();
3762 }
3763 }
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795 @SuppressWarnings({ "unchecked", "rawtypes" })
3796 @GET
3797 @RolesAllowed("users")
3798 @Path("/participants/")
3799 @Produces({MediaType.APPLICATION_JSON})
3800 @ApiOperation(
3801 value = "Returns all suggested participants",
3802 notes = "This method lists all the participants a specific user can invite in a calendar.")
3803 @ApiResponses(value = {
3804 @ApiResponse(code = 200, message = "Successful retrieval of all participants"),
3805 @ApiResponse(code = 503, message = "Can't generate JSON file") })
3806 public Response suggestParticipants(
3807 @ApiParam(value = "The participant name to search for", required = false) @QueryParam("name") String name,
3808 @ApiParam(value = "The starting point when paging through a list of entities", required = false, defaultValue = "0") @QueryParam("offset") int offset,
3809 @ApiParam(value = "The maximum number of results when paging through a list of entities, and do not exceed *hardLimit*. If not specified, *defaultLimit* will be used", required = false) @QueryParam("limit") int limit,
3810 @ApiParam(value = "Tell the service if it must return the total size of the returned collection result, and the *link* http headers", required = false, defaultValue = "false") @QueryParam("returnSize") boolean returnSize,
3811 @ApiParam(value = "This is a list of comma-separated property's names of response json object", required = false) @QueryParam("fields") String fields,
3812 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
3813 @Context UriInfo uri) {
3814 try {
3815 limit = parseLimit(limit);
3816 name = name == null ? "" : name;
3817
3818 ProfileFilter filter = new ProfileFilter();
3819 filter.setName(name);
3820 filter.setCompany("");
3821 filter.setPosition("");
3822 filter.setSkills("");
3823 filter.setExcludedIdentityList(Collections.emptyList());
3824
3825 ListAccess<org.exoplatform.social.core.identity.model.Identity> list = identityManager.getIdentitiesByProfileFilter(OrganizationIdentityProvider.NAME, filter, true);
3826
3827 Collection data = new LinkedList();
3828 if (list != null) {
3829 for (org.exoplatform.social.core.identity.model.Identity identity : list.load(offset, limit)) {
3830 data.add(extractObject(new ParticipantResource(identity), fields));
3831 }
3832 }
3833
3834 CollectionResource parData = new CollectionResource(data, returnSize ? list.getSize() : -1);
3835 parData.setOffset(offset);
3836 parData.setLimit(limit);
3837
3838 ResponseBuilder okResult;
3839 if (jsonp != null) {
3840 JsonValue value = new JsonGeneratorImpl().createJsonObject(parData);
3841 StringBuilder sb = new StringBuilder(jsonp);
3842 sb.append("(").append(value).append(");");
3843 okResult = Response.ok(sb.toString(), new MediaType("text", "javascript"));
3844 } else {
3845 okResult = Response.ok(parData, MediaType.APPLICATION_JSON);
3846 }
3847
3848 if (returnSize) {
3849 okResult.header(HEADER_LINK, buildFullUrl(uri, offset, limit, parData.getSize()));
3850 }
3851
3852
3853 return okResult.cacheControl(nc).build();
3854 } catch (Exception e) {
3855 log.error("Can not suggest participant", e);
3856 }
3857 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3858 }
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884 @SuppressWarnings({ "unchecked", "rawtypes" })
3885 @GET
3886 @RolesAllowed("users")
3887 @Path("/availabilities")
3888 @Produces({MediaType.APPLICATION_JSON})
3889 @ApiOperation(
3890 value = "Return availability of users.",
3891 notes = "(free/busy time)")
3892 @ApiResponses(value = {
3893 @ApiResponse(code = 200, message = "Successful retrieval"),
3894 @ApiResponse(code = 503, message = "Can't generate JSON file") })
3895 public Response getAvailabilities(
3896 @ApiParam(value = "usernames to check availability", required = true) @QueryParam("usernames") String usernames,
3897 @ApiParam(value = "usernames to check availability", required = true) @QueryParam("fromDate") Long fromDate,
3898 @ApiParam(value = "usernames to check availability", required = true) @QueryParam("toDate") Long toDate,
3899 @ApiParam(value = "The name of a JavaScript function to be used as the JSONP callback", required = false) @QueryParam("jsonp") String jsonp,
3900 @Context UriInfo uri) {
3901 try {
3902 Map<String, String> parMap_ = new HashMap<String, String>() ;
3903 parMap_.clear() ;
3904 Map<String, String> tmpMap = new HashMap<String, String>() ;
3905 List<String> newPars = new ArrayList<String>() ;
3906 if(StringUtils.isNotBlank(usernames)) {
3907 for(String par : usernames.split(",")) {
3908 if(orgService.getUserHandler().findUserByName(par) != null) {
3909 String vl = tmpMap.get(par) ;
3910 parMap_.put(par.trim(), vl) ;
3911 if(vl == null) newPars.add(par.trim()) ;
3912 }
3913 }
3914 }
3915 if(newPars.size() > 0) {
3916 EventQuery eventQuery = new EventQuery() ;
3917
3918 CalendarSetting setting = calendarServiceInstance().getCalendarSetting(currentUserId());
3919 TimeZone timeZone = DateUtils.getTimeZone(setting.getTimeZone());
3920
3921 java.util.Calendar from = buildTimeFrom(fromDate, timeZone);
3922 eventQuery.setFromDate(from);
3923
3924 java.util.Calendar to = buildTimeFrom(toDate, timeZone);
3925 eventQuery.setToDate(to);
3926
3927 eventQuery.setParticipants(newPars.toArray(new String[]{})) ;
3928 eventQuery.setNodeType("exo:calendarPublicEvent") ;
3929
3930 Map<String, String> parsMap = calendarServiceInstance().checkFreeBusy(eventQuery);
3931 parMap_.putAll(parsMap) ;
3932 }
3933
3934 ResponseBuilder okResult;
3935 if (jsonp != null) {
3936 JsonValue value = new JsonGeneratorImpl().createJsonObject(parMap_);
3937 StringBuilder sb = new StringBuilder(jsonp);
3938 sb.append("(").append(value).append(");");
3939 okResult = Response.ok(sb.toString(), new MediaType("text", "javascript"));
3940 } else {
3941 okResult = Response.ok(parMap_, MediaType.APPLICATION_JSON);
3942 }
3943
3944
3945 return okResult.cacheControl(nc).build();
3946 } catch (Exception e) {
3947 log.error("Can not check availability", e);
3948 }
3949 return Response.status(HTTPStatus.UNAVAILABLE).cacheControl(nc).build();
3950 }
3951
3952 private java.util.Calendar buildTimeFrom(Long miliseconds, TimeZone timeZone) {
3953 java.util.Calendar cal = java.util.Calendar.getInstance();
3954 cal.setTimeZone(timeZone);
3955 cal.setTimeInMillis(miliseconds);
3956 return cal;
3957 }
3958
3959 private Response buildBadResponse(ErrorResource error) {
3960 return Response.status(HTTPStatus.BAD_REQUEST).entity(error).type(MediaType.APPLICATION_JSON)
3961 .cacheControl(nc).build();
3962 }
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972 private java.util.Calendar[] parseDate(String start, String end) {
3973 java.util.Calendar from = GregorianCalendar.getInstance();
3974 java.util.Calendar to = GregorianCalendar.getInstance();
3975 if(Utils.isEmpty(start)) {
3976 from = java.util.Calendar.getInstance();
3977 from.set(java.util.Calendar.HOUR, 0);
3978 from.set(java.util.Calendar.MINUTE, 0);
3979 from.set(java.util.Calendar.SECOND, 0);
3980 from.set(java.util.Calendar.MILLISECOND, 0);
3981 } else {
3982 from = ISO8601.parse(start);
3983 }
3984 if(Utils.isEmpty(end)) {
3985 to.add(java.util.Calendar.WEEK_OF_MONTH, 1);
3986 to.set(java.util.Calendar.HOUR, 0);
3987 to.set(java.util.Calendar.MINUTE, 0);
3988 to.set(java.util.Calendar.SECOND, 0);
3989 to.set(java.util.Calendar.MILLISECOND, 0);
3990 } else {
3991 to = ISO8601.parse(end);
3992 }
3993 return new java.util.Calendar[] {from, to};
3994 }
3995
3996
3997
3998
3999 private int parseLimit(int limit) {
4000 if (limit <= 0) {
4001 return defaultLimit;
4002 }
4003
4004 if (limit > hardLimit) {
4005 return hardLimit;
4006 }
4007
4008 return limit;
4009 }
4010
4011 private String getBasePath(UriInfo uriInfo) {
4012 StringBuilder path = new StringBuilder(uriInfo.getBaseUri().toString());
4013 path.append(CAL_BASE_URI);
4014 return path.toString();
4015 }
4016
4017 private String buildFullUrl(UriInfo uriInfo, int offset, int limit, long fullSize) {
4018 if (fullSize <= 0) {
4019 return "";
4020 }
4021 offset = offset < 0 ? 0 : offset;
4022
4023 long prev = offset - limit;
4024 prev = offset > 0 && prev < 0 ? 0 : prev;
4025 long prevLimit = offset - prev;
4026
4027 StringBuilder sb = new StringBuilder();
4028 if (prev >= 0) {
4029 sb.append("<").append(uriInfo.getAbsolutePath()).append("?offset=");
4030 sb.append(prev).append("&limit=").append(prevLimit).append(">").append(Utils.SEMICOLON).append("rel=\"previous\",");
4031 }
4032
4033 long next = offset + limit;
4034
4035 if (next < fullSize) {
4036 sb.append("<").append(uriInfo.getAbsolutePath()).append("?offset=");
4037 sb.append(next).append("&limit=").append(limit).append(">").append(Utils.SEMICOLON).append("rel=\"next\",");
4038 }
4039
4040
4041 long firstLimit = limit > fullSize ? fullSize : limit;
4042 sb.append("<").append(uriInfo.getAbsolutePath()).append("?offset=0&limit=").append(firstLimit).append(">");
4043 sb.append(Utils.SEMICOLON).append("rel=\"first\",");
4044
4045 long lastIndex = fullSize - (fullSize % firstLimit);
4046 if (lastIndex == fullSize) {
4047 lastIndex = fullSize - firstLimit;
4048 }
4049 if (lastIndex > 0) {
4050 sb.append("<").append(uriInfo.getAbsolutePath()).append("?offset=").append(lastIndex);
4051 sb.append("&limit=").append(fullSize - lastIndex).append(">");
4052 sb.append(Utils.SEMICOLON).append("rel=\"last\"");
4053 }
4054 if (sb.charAt(sb.length() - 1) == ',') {
4055 sb.deleteCharAt(sb.length() - 1);
4056 }
4057
4058 return sb.toString();
4059 }
4060
4061 private static CalendarService calendarServiceInstance() {
4062 return (CalendarService)ExoContainerContext.getCurrentContainer()
4063 .getComponentInstanceOfType(CalendarService.class);
4064 }
4065
4066
4067
4068
4069
4070
4071
4072
4073 private String makeFeed(String author, List<CalendarEvent> events, FeedData feedData, UriInfo uri) throws Exception {
4074 URI baseUri = uri.getBaseUri();
4075 String baseURL = baseUri.getScheme() + "://" + baseUri.getHost() + ":" + Integer.toString(baseUri.getPort());
4076 String baseRestURL = baseUri.toString();
4077
4078 SyndFeed feed = new SyndFeedImpl();
4079 feed.setFeedType("rss_2.0");
4080 feed.setTitle(feedData.getTitle());
4081 feed.setLink(baseURL + feedData.getUrl());
4082 feed.setDescription(feedData.getTitle());
4083 List<SyndEntry> entries = new ArrayList<SyndEntry>();
4084 SyndEntry entry;
4085 SyndContent description;
4086 for(CalendarEvent event : events) {
4087 if (Utils.EVENT_NUMBER > 0 && Utils.EVENT_NUMBER <= entries.size()) break;
4088 entry = new SyndEntryImpl();
4089 entry.setTitle(event.getSummary());
4090 entry.setLink(baseRestURL + BASE_EVENT_URL + Utils.SLASH + author + Utils.SLASH + event.getId()
4091 + Utils.SPLITTER + event.getCalType() + Utils.ICS_EXT);
4092 entry.setAuthor(author) ;
4093 description = new SyndContentImpl();
4094 description.setType(Utils.MIMETYPE_TEXTPLAIN);
4095 description.setValue(event.getDescription());
4096 entry.setDescription(description);
4097 entries.add(entry);
4098 entry.getEnclosures() ;
4099 }
4100 feed.setEntries(entries);
4101 feed.setEncoding("UTF-8") ;
4102 SyndFeedOutput output = new SyndFeedOutput();
4103 String feedXML = output.outputString(feed);
4104 feedXML = StringUtils.replace(feedXML,"&","&");
4105 return feedXML;
4106 }
4107
4108 private boolean isInGroups(String[] groups) {
4109 Identity identity = ConversationState.getCurrent().getIdentity();
4110 for (String group : groups) {
4111 if (identity.isMemberOf(group)) {
4112 return true;
4113 }
4114 }
4115
4116 return false;
4117 }
4118
4119 private boolean hasViewCalendarPermission(Calendar cal, String username) throws Exception {
4120 if (cal.getCalendarOwner() != null && cal.getCalendarOwner().equals(username)) return true;
4121 else if (cal.getGroups() != null) {
4122 return isInGroups(cal.getGroups());
4123 } else if (cal.getViewPermission() != null) {
4124 return Utils.hasPermission(orgService, cal.getViewPermission(), username);
4125 }
4126 return false;
4127 }
4128
4129 private List<Calendar> findViewableCalendars(String username) throws Exception {
4130 CalendarService service = calendarServiceInstance();
4131
4132 List<Calendar> uCals = service.getUserCalendars(username, true);
4133
4134 Set<String> groupIds = ConversationState.getCurrent().getIdentity().getGroups();
4135 List<GroupCalendarData> gCals = service.getGroupCalendars(groupIds.toArray(new String[groupIds.size()]), true, username);
4136
4137 GroupCalendarData sCals = service.getSharedCalendars(username, true);
4138 if (sCals != null) {
4139 gCals.add(sCals);
4140 }
4141
4142 Calendar[] publicCals = service.getPublicCalendars().load(0, -1);
4143
4144 List<Calendar> results = new LinkedList<Calendar>();
4145 results.addAll(Arrays.asList(publicCals));
4146 for (GroupCalendarData data : gCals) {
4147 if (data.getCalendars() != null) {
4148 for (Calendar cal : data.getCalendars()) {
4149 results.add(cal);
4150 }
4151 }
4152 }
4153 results.addAll(uCals);
4154
4155 return results;
4156 }
4157
4158 private List<Calendar> findEditableCalendars(String username) throws Exception {
4159 List<Calendar> calendars = findViewableCalendars(username);
4160 Iterator<Calendar> iter = calendars.iterator();
4161 while (iter.hasNext()) {
4162 if (!Utils.isCalendarEditable(username, iter.next())) {
4163 iter.remove();
4164 }
4165 }
4166 return calendars;
4167 }
4168
4169 private EventQuery buildEventQuery(String start, String end, String category, List<Calendar> calendars, String calendarPath,
4170 String participant, String eventType) {
4171 java.util.Calendar[] dates = parseDate(start, end);
4172
4173
4174 EventQuery uQuery = new RestEventQuery();
4175 uQuery.setQueryType(Query.SQL);
4176 if (calendarPath != null) {
4177 uQuery.setCalendarPath(calendarPath);
4178 }
4179 List<String> calIds = new LinkedList<String>();
4180 if (calendars != null) {
4181 for (Calendar cal : calendars) {
4182 calIds.add(cal.getId());
4183 }
4184 uQuery.setCalendarId(calIds.toArray(new String[calIds.size()]));
4185 }
4186 if (category != null) {
4187 uQuery.setCategoryId(new String[] {category});
4188 }
4189 if (participant != null) {
4190 uQuery.setParticipants(new String[] {participant});
4191 }
4192 uQuery.setEventType(eventType);
4193 uQuery.setFromDate(dates[0]);
4194 uQuery.setToDate(dates[1]);
4195 uQuery.setOrderType(Utils.ORDER_TYPE_ASCENDING);
4196 uQuery.setOrderBy(new String[]{Utils.ORDERBY_TITLE});
4197 return uQuery;
4198 }
4199
4200 private CalendarEvent findEventAttachment(String attachmentID) throws Exception {
4201 int idx = attachmentID.indexOf("/calendars/");
4202 if (idx != -1) {
4203 int calendars = idx + "/calendars/".length();
4204 int calendar = attachmentID.indexOf('/', calendars) + 1;
4205 int event = attachmentID.indexOf('/', calendar);
4206 if (calendar != -1 && event != -1) {
4207 String eventId = attachmentID.substring(calendar, event);
4208 return calendarServiceInstance().getEventById(eventId);
4209 }
4210 }
4211 return null;
4212 }
4213
4214 private Object extractObject(Resource iv, String fields) {
4215 if (fields != null && iv != null) {
4216 String[] f = fields.split(",");
4217
4218 if (f.length > 0) {
4219 JSONObject obj = new JSONObject(iv);
4220 Map<String, Object> map = new HashMap<String, Object>();
4221
4222 for (String name : f) {
4223 try {
4224 map.put(name, obj.get(name));
4225 } catch (JSONException e) {
4226 log.warn("Can't extract property {} from object {}", name, iv);
4227 }
4228 }
4229 return map;
4230 }
4231 }
4232 return iv;
4233 }
4234
4235 private String currentUserId() {
4236 return ConversationState.getCurrent().getIdentity().getUserId();
4237 }
4238
4239 private Response buildEvent(CalendarEvent old, EventResource evObject, Calendar moveToCal) {
4240 if (moveToCal != null) {
4241 old.setCalendarId(moveToCal.getId());
4242 try {
4243 int calType = calendarServiceInstance().getTypeOfCalendar(currentUserId(), moveToCal.getId());
4244 old.setCalType(String.valueOf(calType));
4245 } catch (Exception ex) {
4246 return buildBadResponse(new ErrorResource("Can not get type of calendar " + moveToCal.getId()));
4247 }
4248 }
4249
4250 try {
4251 if (calendarServiceInstance().isRemoteCalendar(currentUserId(), old.getCalendarId())) {
4252 return buildBadResponse(new ErrorResource("Can not add/update event in remote calendar", "cant-add-event-on-remote-calendar"));
4253 }
4254 } catch (Exception ex) {
4255 return buildBadResponse(new ErrorResource("Can not check remote calendar " + moveToCal.getId(), "cant-check-remote"));
4256 }
4257
4258 String catId = evObject.getCategoryId();
4259 setEventCategory(old, catId);
4260 if (evObject.getDescription() != null) {
4261 old.setDescription(evObject.getDescription());
4262 }
4263 String eventState = evObject.getAvailability();
4264 if (eventState != null) {
4265 if (Arrays.binarySearch(EVENT_AVAILABILITY, eventState) < 0) {
4266 return buildBadResponse(new ErrorResource("availability must be one of " + StringUtils.join(EVENT_AVAILABILITY, ","), "availability"));
4267 } else {
4268 old.setEventState(eventState);
4269 }
4270 }
4271
4272 if (evObject.getParticipants() != null) {
4273 old.setParticipant(evObject.getParticipants());
4274 List<String> parStatus = Stream.of(evObject.getParticipants()).map(par -> {
4275 return par + ":";
4276 }).collect(Collectors.toList());
4277 old.setParticipantStatus(parStatus.toArray(new String[parStatus.size()]));
4278 }
4279
4280 List<Attachment> attachments = new ArrayList<>();
4281 if (old.getAttachment() != null && evObject.getUploadResources() != null) {
4282 for (Attachment att : old.getAttachment()) {
4283 for (org.exoplatform.calendar.ws.bean.UploadResource resource : evObject.getUploadResources()) {
4284 if (att.getName().equals(resource.getName()) && StringUtils.isBlank(resource.getId())) {
4285 attachments.add(att);
4286 }
4287 }
4288 }
4289 }
4290
4291 if (evObject.getUploadResources() != null) {
4292 Stream.of(evObject.getUploadResources()).forEach((upload) -> {
4293 String uploadId = upload.getId();
4294 UploadResource uploadResource = uploadService.getUploadResource(uploadId);
4295 try {
4296 Attachment attachment = new Attachment();
4297 attachment.setInputStream(new FileInputStream(uploadResource.getStoreLocation()));
4298 attachment.setMimeType(uploadResource.getMimeType());
4299 attachment.setName(uploadResource.getFileName());
4300 long fileSize = ((long)uploadResource.getUploadedSize()) ;
4301 attachment.setSize(fileSize);
4302 attachment.setResourceId(uploadId);
4303
4304 attachments.add(attachment);
4305 } catch (Exception ex) {
4306 log.error("Can not save event with upload resource: " + uploadId, ex);
4307 }
4308 });
4309 }
4310 old.setAttachment(attachments);
4311
4312 if (evObject.getRepeat() != null) {
4313 RepeatResource repeat = evObject.getRepeat();
4314 old.setRepeatType(repeat.getType());
4315
4316 if (repeat.getExclude() != null) {
4317 old.setExceptionIds(Arrays.asList(repeat.getExclude()));
4318 }
4319
4320 if (repeat.getRepeatOn() != null) {
4321 String[] reptOns = repeat.getRepeatOn().split(",");
4322 for (String on : reptOns) {
4323 if (Arrays.binarySearch(RP_WEEKLY_BYDAY, on) < 0) {
4324 return buildBadResponse(new ErrorResource("repeatOn can only contains " + StringUtils.join(RP_WEEKLY_BYDAY, ","), "repeatOn"));
4325 }
4326 }
4327 old.setRepeatByDay(reptOns);
4328 }
4329 if (repeat.getRepeateBy() != null) {
4330 String[] repeatBy = repeat.getRepeateBy().split(",");
4331 long[] by = new long[repeatBy.length];
4332 for (int i = 0; i < repeatBy.length; i++) {
4333 try {
4334 by[i] = Integer.parseInt(repeatBy[i]);
4335 if (by[i] < 1 || by[i] > 31) {
4336 return buildBadResponse(new ErrorResource("repeatBy must be >= 1 and <= 31", "repeatBy"));
4337 }
4338 } catch (Exception e) {
4339 log.debug(e);
4340 }
4341 }
4342 old.setRepeatByMonthDay(by);
4343 }
4344
4345 Date untilDate = null;
4346 long count = 0;
4347 if (repeat.getEnd() != null) {
4348 End end = repeat.getEnd();
4349 String val = end.getValue();
4350
4351 if (RP_END_AFTER.equals(end.getType())) {
4352 try {
4353 count = Long.parseLong(val);
4354 } catch (Exception ex) {
4355 log.debug("Can not set repeat count, count is invalid: {}", val);
4356 }
4357 } else if (RP_END_BYDATE.equals(end.getType())) {
4358 try {
4359 untilDate = ISO8601.parse(val).getTime();
4360 } catch (Exception e) {
4361 log.debug("Can not set repeat until date, date is invalid: {}", val);
4362 }
4363 }
4364 }
4365 old.setRepeatUntilDate(untilDate);
4366 old.setRepeatCount(count);
4367
4368 int every = repeat.getEvery();
4369 if (every < 1 || every > 30) {
4370 every = 1;
4371 }
4372 old.setRepeatInterval(repeat.getEvery());
4373 } else {
4374 old.setRepeatType(Event.RP_NOREPEAT);
4375 }
4376 old.setRecurrenceId(evObject.getRecurrenceId());
4377
4378 java.util.Calendar[] fromTo = parseDate(evObject.getFrom(), evObject.getTo());
4379 if (fromTo[0].after(fromTo[1]) || fromTo[0].equals(fromTo[1])) {
4380 return buildBadResponse(new ErrorResource("\"from\" date must be before \"to\" date", "event-date-time-logic"));
4381 }
4382 old.setFromDateTime(fromTo[0].getTime());
4383 if (evObject.getLocation() != null) {
4384 old.setLocation(evObject.getLocation());
4385 }
4386 String priority = evObject.getPriority();
4387 if (priority != null) {
4388 if (Arrays.binarySearch(PRIORITY, priority) < 0) {
4389 return buildBadResponse(new ErrorResource("priority must be one of " + StringUtils.join(PRIORITY, ","), "priority"));
4390 } else {
4391 old.setPriority(evObject.getPriority());
4392 }
4393 }
4394 if (evObject.getReminder() != null) {
4395 Arrays.stream(evObject.getReminder()).forEach(reminder -> {
4396 if (reminder.getReminderOwner() == null) {
4397 reminder.setReminderOwner(currentUserId());
4398 }
4399 try {
4400 if (reminder.getReminderType().equals(Reminder.TYPE_EMAIL) &&
4401 reminder.getEmailAddress() == null) {
4402 String email = orgService.getUserHandler().findUserByName(currentUserId()).getEmail();
4403 reminder.setEmailAddress(email);
4404 }
4405 } catch (Exception ex) {
4406 log.error("Can not set default email for reminder of user " + currentUserId(), ex);
4407 }
4408 if (reminder.getFromDateTime() == null) {
4409 reminder.setFromDateTime(old.getFromDateTime());
4410 }
4411 });
4412 old.setReminders(Arrays.asList(evObject.getReminder()));
4413 }
4414 String privacy = evObject.getPrivacy();
4415 if (privacy != null) {
4416 if (!CalendarEvent.IS_PRIVATE.equals(privacy) && !CalendarEvent.IS_PUBLIC.equals(privacy)) {
4417 return buildBadResponse(new ErrorResource("privacy can only be public or private", "privacy"));
4418 } else {
4419 old.setPrivate(CalendarEvent.IS_PRIVATE.equals(privacy));
4420 }
4421 }
4422 String subject = evObject.getSubject();
4423 if (subject != null) {
4424 subject = subject.trim();
4425 if (subject.isEmpty()) {
4426 return buildBadResponse(new ErrorResource("subject must not be empty", "subject"));
4427 } else {
4428 old.setSummary(subject);
4429 }
4430 }
4431 old.setToDateTime(fromTo[1].getTime());
4432 return null;
4433 }
4434
4435 private Response buildEventFromTask(CalendarEvent old, TaskResource evObject) {
4436 String catId = evObject.getCategoryId();
4437 setEventCategory(old, catId);
4438 if (evObject.getNote() != null) {
4439 old.setDescription(evObject.getNote());
4440 }
4441 java.util.Calendar[] fromTo = parseDate(evObject.getFrom(), evObject.getTo());
4442 if (fromTo[0].after(fromTo[1]) || fromTo[0].equals(fromTo[1])) {
4443 return buildBadResponse(new ErrorResource("\"from\" date must be before \"to\" date", "from"));
4444 }
4445 old.setFromDateTime(fromTo[0].getTime());
4446 String priority = evObject.getPriority();
4447 if (priority != null) {
4448 if (Arrays.binarySearch(PRIORITY, priority) < 0) {
4449 return buildBadResponse(new ErrorResource("priority must be one of " + StringUtils.join(PRIORITY, ","), "priority"));
4450 } else {
4451 old.setPriority(evObject.getPriority());
4452 }
4453 }
4454 if (evObject.getReminder() != null) {
4455 old.setReminders(Arrays.asList(evObject.getReminder()));
4456 }
4457 String status = evObject.getStatus();
4458 if (status != null && !status.isEmpty()) {
4459 if (Arrays.binarySearch(TASK_STATUS, status) < 0) {
4460 return buildBadResponse(new ErrorResource("status must be one of " + StringUtils.join(TASK_STATUS, ","), "status"));
4461 } else {
4462 old.setStatus(status);
4463
4464 old.setEventState(status);
4465 }
4466 }
4467 String name = evObject.getName();
4468 if (name != null) {
4469 name = name.trim();
4470 if (name.isEmpty()) {
4471 return buildBadResponse(new ErrorResource("name must not be empty", "name"));
4472 } else {
4473 old.setSummary(evObject.getName());
4474 }
4475 }
4476 old.setToDateTime(fromTo[1].getTime());
4477 if (evObject.getDelegation() != null) {
4478 old.setTaskDelegator(StringUtils.join(evObject.getDelegation(), ","));
4479 }
4480 return null;
4481 }
4482
4483 private void setEventCategory(CalendarEvent old, String catId) {
4484 if (catId != null) {
4485 try {
4486 EventCategory cat = calendarServiceInstance().getEventCategory(currentUserId(), catId);
4487 if (cat != null) {
4488 old.setEventCategoryId(cat.getId());
4489 old.setEventCategoryName(cat.getName());
4490 }
4491 } catch (Exception e) {
4492 log.debug(e.getMessage(), e);
4493 }
4494 }
4495 }
4496
4497 private Response buildCalendar(Calendar cal, CalendarResource calR) {
4498 if (calR.getColor() != null) {
4499 cal.setCalendarColor(calR.getColor());
4500 }
4501 if (calR.getOwner() != null) {
4502 cal.setCalendarOwner(calR.getOwner());
4503 }
4504 if (calR.getDescription() != null) {
4505 cal.setDescription(calR.getDescription());
4506 }
4507 Set<String> viewPermissions = new HashSet<String>();
4508 if (calR.getEditPermission() != null && !calR.getEditPermission().isEmpty()) {
4509 cal.setEditPermission(calR.getEditPermission().split(Utils.SEMICOLON));
4510 for (String permission : cal.getEditPermission()) {
4511 viewPermissions.add(permission);
4512 }
4513 }
4514
4515 if (calR.getViewPermission() != null && !calR.getViewPermission().isEmpty()) {
4516 for (String permission : calR.getViewPermission().split(Utils.SEMICOLON)) {
4517 viewPermissions.add(permission);
4518 }
4519 }
4520 if (viewPermissions.size() > 0) {
4521 cal.setViewPermission(viewPermissions.toArray(new String[viewPermissions.size()]));
4522 }
4523
4524 if (calR.getGroups() != null) {
4525 cal.setGroups(calR.getGroups());
4526 }
4527 String name = calR.getName();
4528 if (name != null) {
4529 name = name.trim();
4530 if (name.isEmpty() || containSpecialChar(name)) {
4531 return buildBadResponse(new ErrorResource("calendar name is empty or contains special characters", "name"));
4532 } else {
4533 cal.setName(calR.getName());
4534 }
4535 }
4536 if (calR.getPrivateURL() != null) {
4537 cal.setPrivateUrl(calR.getPrivateURL());
4538 }
4539 if (calR.getPublicURL() != null) {
4540 cal.setPublicUrl(calR.getPublicURL());
4541 }
4542 if (calR.getTimeZone() != null) {
4543 cal.setTimeZone(calR.getTimeZone());
4544 }
4545 return null;
4546 }
4547
4548 private boolean containSpecialChar(String value) {
4549 for (int i = 0; i < value.length(); i++) {
4550 char c = value.charAt(i);
4551 if (Character.isLetter(c) || Character.isDigit(c) || c == '_' || c == '-' || Character.isSpaceChar(c)) {
4552 continue;
4553 }
4554 return true;
4555 }
4556 return false;
4557 }
4558
4559 private ResponseBuilder buildJsonP(Object resource, String jsonp) throws Exception {
4560 ResponseBuilder response = null;
4561 if (jsonp != null) {
4562 String json = null;
4563 if (resource instanceof Map) json = new JSONObject((Map<?, ?>)resource).toString();
4564 else {
4565 JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl();
4566 json = generatorImpl.createJsonObject(resource).toString();
4567 }
4568 StringBuilder sb = new StringBuilder(jsonp);
4569 sb.append("(").append(json).append(");");
4570 response = Response.ok(sb.toString(), new MediaType("text", "javascript")).cacheControl(nc);
4571 } else {
4572 response = Response.ok(resource, MediaType.APPLICATION_JSON).cacheControl(nc);
4573 }
4574
4575 return response;
4576 }
4577
4578 private Object buildTaskResource(CalendarEvent event,
4579 UriInfo uriInfo,
4580 String expand,
4581 String fields) throws Exception {
4582 CalendarService service = calendarServiceInstance();
4583 String basePath = getBasePath(uriInfo);
4584 TaskResource evtResource = new TaskResource(event, basePath);
4585
4586 List<Expand> expands = Expand.parse(expand);
4587 for (Expand exp : expands) {
4588 if ("calendar".equals(exp.getField())) {
4589 Calendar cal = service.getCalendarById(event.getCalendarId());
4590 setCalType(cal);
4591 evtResource.setCal(new CalendarResource(cal, basePath));
4592 } else if ("categories".equals(exp.getField())) {
4593 String categoryId = event.getEventCategoryId();
4594 if (categoryId != null) {
4595 EventCategory evCat = service.getEventCategory(currentUserId(), categoryId);
4596 if (evCat != null) {
4597 CategoryResource[] catRs = new CategoryResource[] {new CategoryResource(evCat, basePath)};
4598 evtResource.setCats(Utils.<CategoryResource>subArray(catRs, exp.getOffset(), exp.getLimit()));
4599 }
4600 }
4601 } else if ("attachments".equals(exp.getField())) {
4602 if (event.getAttachment() != null) {
4603 List<AttachmentResource> attRs = new LinkedList<AttachmentResource>();
4604 for (Attachment att : event.getAttachment()) {
4605 attRs.add(new AttachmentResource(att, basePath));
4606 }
4607 attRs = Utils.subList(attRs, exp.getOffset(), exp.getLimit());
4608 evtResource.setAtts(attRs.toArray(new AttachmentResource[attRs.size()]));
4609 }
4610 }
4611 }
4612
4613 return extractObject(evtResource, fields);
4614 }
4615
4616 private Object buildEventResource(CalendarEvent ev, UriInfo uriInfo, String expand, String fields) throws Exception {
4617 CalendarService service = calendarServiceInstance();
4618 String basePath = getBasePath(uriInfo);
4619 EventResource evtResource = new EventResource(ev, basePath);
4620
4621 List<Expand> expands = Expand.parse(expand);
4622 for (Expand exp : expands) {
4623 if ("calendar".equals(exp.getField())) {
4624 Calendar cal = service.getCalendarById(ev.getCalendarId());
4625 setCalType(cal);
4626 evtResource.setCal(new CalendarResource(cal, basePath));
4627 } else if ("categories".equals(exp.getField())) {
4628 String categoryId = ev.getEventCategoryId();
4629 if (categoryId != null) {
4630 EventCategory evCat = service.getEventCategory(currentUserId(), categoryId);
4631 if (evCat != null) {
4632 CategoryResource[] catRs = new CategoryResource[] {new CategoryResource(evCat, basePath)};
4633 evtResource.setCats(Utils.<CategoryResource>subArray(catRs, exp.getOffset(), exp.getLimit()));
4634 }
4635 }
4636 } else if ("originalEvent".equals(exp.getField())) {
4637 String orgId = ev.getOriginalReference();
4638 if (orgId != null) {
4639 CalendarEvent orgEv = service.getEventById(orgId);
4640 if (orgEv != null) {
4641 evtResource.setOEvent(new EventResource(orgEv, basePath));
4642 }
4643 }
4644 } else if ("attachments".equals(exp.getField())) {
4645 if (ev.getAttachment() != null) {
4646 List<AttachmentResource> attRs = new LinkedList<AttachmentResource>();
4647 for (Attachment att : ev.getAttachment()) {
4648 attRs.add(new AttachmentResource(att, basePath));
4649 }
4650 attRs = Utils.subList(attRs, exp.getOffset(), exp.getLimit());
4651 evtResource.setAtts(attRs.toArray(new AttachmentResource[attRs.size()]));
4652 }
4653 }
4654 }
4655
4656 return extractObject(evtResource, fields);
4657 }
4658
4659 private Object buildFeedResource(FeedData feed, List<String> calIds, UriInfo uriInfo,
4660 String expand, String fields) throws Exception {
4661 CalendarService service = calendarServiceInstance();
4662 String basePath = getBasePath(uriInfo);
4663 FeedResource feedResource = new FeedResource(feed, calIds.toArray(new String[calIds.size()]), basePath);
4664
4665 List<Expand> expands = Expand.parse(expand);
4666 for (Expand exp : expands) {
4667 if ("calendars".equals(exp.getField())) {
4668 List<Serializable> calendars = new ArrayList<Serializable>();
4669 for(String calId : Utils.subList(calIds, exp.getOffset(), exp.getLimit())) {
4670 Calendar cal = service.getCalendarById(calId);
4671 setCalType(cal);
4672 calendars.add(new CalendarResource(cal, getBasePath(uriInfo)));
4673 }
4674 feedResource.setCals(Utils.subList(calendars, exp.getOffset(), exp.getLimit()));
4675 }
4676 }
4677
4678 return extractObject(feedResource, fields);
4679 }
4680
4681 private Object buildInvitationResource(Invitation invitation,
4682 UriInfo uriInfo,
4683 String expand,
4684 String fields) throws Exception {
4685 CalendarService service = calendarServiceInstance();
4686 String basePath = getBasePath(uriInfo);
4687 InvitationResource ivtResource = new InvitationResource(invitation, basePath);
4688
4689 List<Expand> expands = Expand.parse(expand);
4690 for (Expand exp : expands) {
4691 if ("event".equals(exp.getField())) {
4692 CalendarEvent event = service.getEventById(invitation.getEventId());
4693 ivtResource.setEvt(new EventResource(event, basePath));
4694 }
4695 }
4696
4697 return extractObject(ivtResource, fields);
4698 }
4699
4700 private String digest(byte[] data) throws Exception {
4701 MessageDigest md5 = MessageDigest.getInstance("MD5");
4702 byte[] hashCode = md5.digest(data);
4703
4704 return String.valueOf(hashCode);
4705 }
4706
4707 private void setCalType(Calendar cal) {
4708 try {
4709 cal.setCalType(calendarServiceInstance().getTypeOfCalendar(currentUserId(), cal.getId()));
4710 } catch (Exception e) {
4711 log.error(e);
4712 }
4713 }
4714
4715 private void saveEvent(int calType, CalendarEvent event, boolean bln) throws Exception {
4716 saveEvent(event.getCalendarId(), event.getCalendarId(), calType, calType, event, null, bln);
4717 }
4718
4719 private void saveEvent(String fromCal, String toCal, int fromType, int toType, CalendarEvent event, RecurringUpdateType recurringUpdateType, boolean bln) throws Exception {
4720 CalendarService calService = calendarServiceInstance();
4721 if (recurringUpdateType == null) {
4722
4723 if (bln) {
4724 switch (toType) {
4725 case Calendar.TYPE_PRIVATE:
4726 calService.saveUserEvent(currentUserId(), event.getCalendarId(), event, bln);
4727 break;
4728 case Calendar.TYPE_PUBLIC:
4729 calService.savePublicEvent(event.getCalendarId(), event, bln);
4730 break;
4731 case Calendar.TYPE_SHARED:
4732 calService.saveEventToSharedCalendar(currentUserId(), event.getCalendarId(), event,bln);
4733 break;
4734 default:
4735 break;
4736 }
4737 } else {
4738 List<CalendarEvent> listEvent = Arrays.asList(event);
4739 if (org.exoplatform.calendar.service.Utils.isExceptionOccurrence(event)) {
4740 calService.updateOccurrenceEvent(fromCal, toCal, String.valueOf(fromType), String.valueOf(toType), listEvent, currentUserId());
4741 } else {
4742 calService.moveEvent(fromCal, toCal, String.valueOf(fromType), String.valueOf(toType), listEvent, currentUserId()) ;
4743 }
4744 }
4745 } else {
4746
4747 CalendarEvent original = calService.getRepetitiveEvent(event);
4748 recurringUpdateType = recurringUpdateType == null ? RecurringUpdateType.ONE : recurringUpdateType;
4749 switch (recurringUpdateType) {
4750 case ALL:
4751 calService.saveAllSeriesEvents(event, currentUserId());
4752 break;
4753 case FOLLOWING:
4754 calService.saveFollowingSeriesEvents(original, event, currentUserId());
4755 break;
4756 case ONE:
4757 calService.saveOneOccurrenceEvent(original, event, currentUserId());
4758 break;
4759 }
4760 }
4761 }
4762
4763 public static class Expand {
4764 private String field;
4765 private int offset;
4766 private int limit;
4767
4768 public Expand(String field, int offset, int limit) {
4769 this.field = field;
4770 this.offset = offset;
4771 this.limit = limit;
4772 }
4773
4774 public static List<Expand> parse(String expand) {
4775 List<Expand> expands = new LinkedList<Expand>();
4776
4777 if (expand != null) {
4778 String[] frags = expand.split(",");
4779 List<String> tmp = new LinkedList<String>();
4780 for (int i = 0; i < frags.length; i++) {
4781 String str = frags[i].trim();
4782 if (!str.contains("(") && str.contains("")) {
4783 tmp.add(str);
4784 } else if (str.contains("(") && i + 1 < frags.length) {
4785 tmp.add(str + "," + frags[++i]);
4786 }
4787 }
4788
4789 for (String exp : tmp) {
4790 String fieldName = null;
4791 int offset = 0;
4792 int limit = -1;
4793 if (exp != null) {
4794 exp = exp.trim();
4795 int i =exp.indexOf('(');
4796 if (i > 0) {
4797 fieldName = exp.substring(0, i).trim();
4798 try {
4799 offset = Integer.parseInt(exp.substring(exp.indexOf("offset:") + "offset:".length(), exp.indexOf(",")).trim());
4800 limit = Integer.parseInt(exp.substring(exp.indexOf("limit:") + "limit:".length(), exp.indexOf(")")).trim());
4801 } catch (Exception ex) {
4802 log.debug(ex);
4803 }
4804 } else {
4805 fieldName = exp;
4806 }
4807 }
4808
4809 expands.add(new Expand(fieldName, offset, limit));
4810 }
4811 }
4812
4813 return expands;
4814 }
4815
4816 public String getField() {
4817 return field;
4818 }
4819
4820 public int getOffset() {
4821 return offset;
4822 }
4823
4824 public int getLimit() {
4825 return limit;
4826 }
4827 }
4828 }