Java tutorial
/********************************************************************************** * $URL: $ * $Id$ *********************************************************************************** * * Copyright (c) 2011 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.osedu.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.dash.entity; import java.lang.reflect.InvocationTargetException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TimeZone; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.announcement.api.AnnouncementMessage; import org.sakaiproject.calendar.api.Calendar; import org.sakaiproject.calendar.api.CalendarEvent; import org.sakaiproject.calendar.api.CalendarService; import org.sakaiproject.calendar.api.RecurrenceRule; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.dash.listener.EventProcessor; import org.sakaiproject.dash.logic.DashboardLogic; import org.sakaiproject.dash.app.SakaiProxy; import org.sakaiproject.dash.model.CalendarItem; import org.sakaiproject.dash.model.Context; import org.sakaiproject.dash.model.RepeatingCalendarItem; import org.sakaiproject.dash.model.SourceType; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityPropertyNotDefinedException; import org.sakaiproject.entity.api.EntityPropertyTypeException; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.event.api.Event; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.time.api.TimeRange; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.user.api.User; import org.sakaiproject.util.ResourceLoader; /** * THIS WILL BE MOVED TO THE calendar PROJECT IN SAKAI CORE ONCE THE INTERFACE IS MOVED TO KERNEL. * * When this moves to calendar project, eliminate references to SakaiProxy and use sakai services instead. */ public class ScheduleSupport { private Log logger = LogFactory.getLog(ScheduleSupport.class); ResourceLoader rl = new ResourceLoader("dash_entity"); // TODO: add member variable and setter for defaultImageForEvent that will allow DEFAULT_IMAGE_FOR_EVENT // TODO: to be overridden by spring injection. Then use DEFAULT_IMAGE_FOR_EVENT only if defaultImageForEvent // TODO: is not set. public static final String DEFAULT_IMAGE_FOR_EVENT = "/library/image/sakai/activity.gif"; protected SakaiProxy sakaiProxy; public void setSakaiProxy(SakaiProxy sakaiProxy) { this.sakaiProxy = sakaiProxy; } protected TimeService timeService; public void setTimeService(TimeService timeService) { this.timeService = timeService; } protected CalendarService calendarService; public void setCalendarService(CalendarService calendarService) { this.calendarService = calendarService; } protected SiteService siteService; public void setSiteService(SiteService siteService) { this.siteService = siteService; } protected DashboardLogic dashboardLogic; public void setDashboardLogic(DashboardLogic dashboardLogic) { this.dashboardLogic = dashboardLogic; } protected Map<String, String> scheduleEventTypeMap; protected Map<String, String> eventTypeImageUrlMap; protected RepeatingEventGenerator scheduleEntityType; public static final String IDENTIFIER = "schedule"; public void init() { logger.info("init()"); this.scheduleEntityType = new ScheduleEntityType(); this.dashboardLogic.registerEntityType(scheduleEntityType); this.dashboardLogic.registerEventProcessor(new ScheduleNewEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleRemoveEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateTimeEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateTitleEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateTypeEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleReviseEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateAccessEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateFrequencyEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateExcludedEventProcessor()); this.dashboardLogic.registerEventProcessor(new ScheduleUpdateExclusionsEventProcessor()); scheduleEventTypeMap = new HashMap<String, String>(); scheduleEventTypeMap.put("Academic Calendar", "schedule.key1"); scheduleEventTypeMap.put("Activity", "schedule.key2"); scheduleEventTypeMap.put("Cancellation", "schedule.key3"); scheduleEventTypeMap.put("Class section - Discussion", "schedule.key4"); scheduleEventTypeMap.put("Class section - Lab", "schedule.key5"); scheduleEventTypeMap.put("Class section - Lecture", "schedule.key6"); scheduleEventTypeMap.put("Class section - Small Group", "schedule.key7"); scheduleEventTypeMap.put("Class session", "schedule.key8"); scheduleEventTypeMap.put("Computer Session", "schedule.key9"); scheduleEventTypeMap.put("Deadline", "schedule.key10"); scheduleEventTypeMap.put("Exam", "schedule.key11"); scheduleEventTypeMap.put("Meeting", "schedule.key12"); scheduleEventTypeMap.put("Multidisciplinary Conference", "schedule.key13"); scheduleEventTypeMap.put("Quiz", "schedule.key14"); scheduleEventTypeMap.put("Special event", "schedule.key15"); scheduleEventTypeMap.put("Web Assignment", "schedule.key16"); eventTypeImageUrlMap = new HashMap<String, String>(); eventTypeImageUrlMap.put("Academic Calendar", "/library/image/sakai/academic_calendar.gif"); eventTypeImageUrlMap.put("Activity", "/library/image/sakai/activity.gif"); eventTypeImageUrlMap.put("Cancellation", "/library/image/sakai/cancelled.gif"); eventTypeImageUrlMap.put("Class section - Discussion", "/library/image/sakai/class_dis.gif"); eventTypeImageUrlMap.put("Class section - Lab", "/library/image/sakai/class_lab.gif"); eventTypeImageUrlMap.put("Class section - Lecture", "/library/image/sakai/class_lec.gif"); eventTypeImageUrlMap.put("Class section - Small Group", "/library/image/sakai/class_sma.gif"); eventTypeImageUrlMap.put("Class session", "/library/image/sakai/class_session.gif"); eventTypeImageUrlMap.put("Computer Session", "/library/image/sakai/computersession.gif"); eventTypeImageUrlMap.put("Deadline", "/library/image/sakai/deadline.gif"); eventTypeImageUrlMap.put("Exam", "/library/image/silk/accept.png"); eventTypeImageUrlMap.put("Meeting", "/library/image/sakai/meeting.gif"); eventTypeImageUrlMap.put("Multidisciplinary Conference", "/library/image/sakai/multi-conference.gif"); eventTypeImageUrlMap.put("Quiz", "/library/image/silk/star.png"); eventTypeImageUrlMap.put("Special event", "/library/image/sakai/special_event.gif"); eventTypeImageUrlMap.put("Web Assignment", "/library/image/sakai/webassignment.gif"); } /** * Inner class: ScheduleEntityType * @author zqian * */ public class ScheduleEntityType implements RepeatingEventGenerator { public static final String LABEL_METADATA = "calendar_metadata-label"; /* (non-Javadoc) * @see org.sakaiproject.dash.entity.EntityType#getIdentifier() */ public String getIdentifier() { return IDENTIFIER; } /* (non-Javadoc) * @see org.sakaiproject.dash.entity.EntityType#getValues(java.lang.String, java.lang.String) */ public Map<String, Object> getValues(String entityReference, String localeCode) { Map<String, Object> values = new HashMap<String, Object>(); CalendarEvent cEvent = (CalendarEvent) sakaiProxy.getEntity(entityReference); if (cEvent != null) { DateFormat df = DateFormat.getDateTimeInstance(); ResourceProperties props = cEvent.getProperties(); // "entity-type": "assignment" values.put(DashboardEntityInfo.VALUE_ENTITY_TYPE, IDENTIFIER); values.put(VALUE_TITLE, cEvent.getDisplayName()); values.put(VALUE_CALENDAR_TIME, df.format(new Date(cEvent.getRange().firstTime().getTime()))); try { values.put(VALUE_NEWS_TIME, df.format( new Date(props.getTimeProperty(ResourceProperties.PROP_CREATION_DATE).getTime()))); } catch (EntityPropertyNotDefinedException e) { logger.warn("getValues(" + entityReference + "," + localeCode + ") EntityPropertyNotDefinedException: " + e); } catch (EntityPropertyTypeException e) { logger.warn("getValues(" + entityReference + "," + localeCode + ") EntityPropertyTypeException: " + e); } values.put(DashboardEntityInfo.VALUE_ENTITY_TYPE, IDENTIFIER); values.put(VALUE_DESCRIPTION, cEvent.getDescription()); // "user-name": "Creator's Name" /*User user = cEvent.getCreator().getFrom(); if(user != null) { values.put(VALUE_USER_NAME, user.getDisplayName()); }*/ // more info List<Map<String, String>> infoList = new ArrayList<Map<String, String>>(); Map<String, String> infoItem = new HashMap<String, String>(); infoItem.put(VALUE_INFO_LINK_URL, sakaiProxy.getScheduleEventUrl(cEvent.getReference())); infoItem.put(VALUE_INFO_LINK_TITLE, rl.getString("schedule.info.link")); infoItem.put(VALUE_INFO_LINK_TARGET, "_top"); infoList.add(infoItem); values.put(VALUE_MORE_INFO, infoList); // "attachments": [ ... ] List<Reference> attachments = cEvent.getAttachments(); if (attachments != null && !attachments.isEmpty()) { List<Map<String, String>> attList = new ArrayList<Map<String, String>>(); for (Reference ref : attachments) { ContentResource resource = (ContentResource) ref.getEntity(); Map<String, String> attInfo = new HashMap<String, String>(); attInfo.put(VALUE_ATTACHMENT_TITLE, resource.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME)); attInfo.put(VALUE_ATTACHMENT_URL, resource.getUrl()); attInfo.put(VALUE_ATTACHMENT_MIMETYPE, resource.getContentType()); attInfo.put(VALUE_ATTACHMENT_SIZE, Long.toString(resource.getContentLength())); attInfo.put(VALUE_ATTACHMENT_TARGET, sakaiProxy.getTargetForMimetype(resource.getContentType())); attList.add(attInfo); } values.put(VALUE_ATTACHMENTS, attList); } } return values; } /* (non-Javadoc) * @see org.sakaiproject.dash.entity.EntityType#getProperties(java.lang.String, java.lang.String) */ public Map<String, String> getProperties(String entityReference, String localeCode) { ResourceLoader rl = new ResourceLoader("dash_entity"); Map<String, String> props = new HashMap<String, String>(); props.put(LABEL_METADATA, rl.getString("schedule.metadata")); return props; } /* (non-Javadoc) * @see org.sakaiproject.dash.entity.EntityType#getOrder(java.lang.String, java.lang.String) */ public List<List<String>> getOrder(String entityReference, String localeCode) { List<List<String>> order = new ArrayList<List<String>>(); List<String> section0 = new ArrayList<String>(); section0.add(VALUE_TITLE); order.add(section0); List<String> section1 = new ArrayList<String>(); section1.add(LABEL_METADATA); order.add(section1); List<String> section2 = new ArrayList<String>(); section2.add(VALUE_DESCRIPTION); order.add(section2); List<String> section3 = new ArrayList<String>(); section3.add(VALUE_ATTACHMENTS); order.add(section3); List<String> section4 = new ArrayList<String>(); section4.add(VALUE_MORE_INFO); order.add(section4); return order; } public void init() { logger.info("init()"); } public boolean isAvailable(String entityReference) { boolean rv = false; if (entityReference == null) { logger.warn(this + "isAvailable() invoked with null entity reference"); } else { CalendarEvent cEvent = (CalendarEvent) sakaiProxy.getEntity(entityReference); if (cEvent != null) { String calendarRef = cEvent.getCalendarReference(); try { Calendar calendar = calendarService.getCalendar(calendarRef); String siteId = calendar.getContext(); try { Site s = siteService.getSite(siteId); if (s.isPublished()) { rv = true; } } catch (IdUnusedException exception) { logger.warn(this + " isAvailable: cannot find site " + siteId + " " + exception.getMessage()); } } catch (IdUnusedException exception) { logger.warn(this + " isAvailable: cannot find calendar " + calendarRef + " " + exception.getMessage()); } catch (PermissionException exception) { logger.warn(this + " isAvailable: don't have permission to get calendar " + calendarRef + " " + exception.getMessage()); } } } return rv; } public boolean isUserPermitted(String sakaiUserId, String entityReference, String contextId) { // use message read permission String accessPermission = SakaiProxy.PERMIT_SCHEDULE_ACCESS; List users = sakaiProxy.unlockUsers(accessPermission, sakaiProxy.getSiteReference(contextId)); for (Object user : users) { if (sakaiUserId.equals(((User) user).getId())) { // user can submit return true; } } return false; } /** * {@inheritDoc} */ public String getEventDisplayString(String key, String dflt) { ResourceLoader rl = new ResourceLoader("dash_entity"); return rl.getString(key, dflt); } /* * (non-Javadoc) * @see org.sakaiproject.dash.entity.RepeatingEventGenerator#generateRepeatingEventDates(java.lang.String, java.util.Date, java.util.Date) */ public Map<Integer, Date> generateRepeatingEventDates(String entityReference, Date beginDate, Date endDate) { if (logger.isDebugEnabled()) { logger.debug("generateRepeatingEventDates(" + entityReference + ", " + endDate + ") "); } Map<Integer, Date> dateMap = new HashMap<Integer, Date>(); CalendarEvent cEvent = (CalendarEvent) sakaiProxy.getEntity(entityReference); if (cEvent != null) { Date now = new Date(); TimeRange range2 = timeService.newTimeRange(now.getTime(), endDate.getTime() - now.getTime()); TimeZone timezone = timeService.getLocalTimeZone(); Integer first = null; Integer last = null; List objects = cEvent.getRecurrenceRule().generateInstances(cEvent.getRange(), range2, timezone); cEvent.getRecurrenceRule().excludeInstances(objects); for (Object obj : objects) { try { // the following use of reflection would not be necessary if // sakai.schedule exposed these methods in the API .... TimeRange range = (TimeRange) obj.getClass().getMethod("getRange", null).invoke(obj, null); Integer sequence = (Integer) obj.getClass().getMethod("getSequence", null).invoke(obj, null); dateMap.put(sequence, new Date(range.firstTime().getTime())); if (first == null || first.intValue() > sequence.intValue()) { first = sequence; } // actually, this is not necessary. // the dates come to us in order (but the API doesn't guarantee that). if (last == null || last.intValue() < sequence.intValue()) { last = sequence; } if (logger.isDebugEnabled()) { logger.debug(" " + sequence + " --> " + dateMap.get(sequence)); } } catch (NoSuchMethodException e) { logger.warn("NoSuchMethodException while generating a list of dates for a recurring event: " + e); } catch (IllegalArgumentException e) { logger.warn( "IllegalArgumentException while generating a list of dates for a recurring event: " + e); } catch (SecurityException e) { logger.warn( "SecurityException while generating a list of dates for a recurring event: " + e); } catch (IllegalAccessException e) { logger.warn( "IllegalAccessException while generating a list of dates for a recurring event: " + e); } catch (InvocationTargetException e) { logger.warn( "InvocationTargetException while generating a list of dates for a recurring event: " + e); } } } return dateMap; } public String getGroupTitle(int numberOfItems, String contextTitle, String labelKey) { ResourceLoader rl = new ResourceLoader("dash_entity"); Object[] args = new Object[] { numberOfItems, contextTitle }; return rl.getFormattedMessage("announcement.grouped.title", args); } public String getIconUrl(String subtype) { String url = eventTypeImageUrlMap.get(subtype); if (url == null) { // default url to activity.gif url = DEFAULT_IMAGE_FOR_EVENT; } return url; } public List<String> getUsersWithAccess(String entityReference) { Collection<String> users = null; users = sakaiProxy.getAuthorizedUsers(SakaiProxy.PERMIT_SCHEDULE_ACCESS, entityReference); List<String> rv = new ArrayList<String>(); if (users != null && !users.isEmpty()) { rv.addAll(users); } return rv; } } /** * Inner class: ScheduleNewEventProcessor * @author zqian * */ public class ScheduleNewEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleNewEventProcessor.class); /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#getEventIdentifer() */ public String getEventIdentifer() { return SakaiProxy.EVENT_SCHEDULE_NEW_EVENT; } /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#processEvent(org.sakaiproject.event.api.Event) */ public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("removing links and item for " + event.getResource()); } String eventId = event.getEvent(); String eventContextString = event.getContext(); Entity entity = sakaiProxy.getEntity(event.getResource()); // handle add events if (entity != null && entity instanceof CalendarEvent) { CalendarEvent cEvent = (CalendarEvent) entity; String cEventReference = cEvent.getReference(); Context context = dashboardLogic.getContext(eventContextString); if (context != null) { boolean sitePublished = false; try { Site s = siteService.getSite(context.getContextId()); if (s.isPublished()) { sitePublished = true; } } catch (IdUnusedException exception) { logger.warn(this + " ScheduleNewEventProcessor.processEvent(): cannot find site " + context.getContextId() + " " + exception.getMessage()); } SourceType sourceType = dashboardLogic.getSourceType(IDENTIFIER); // Third parameter in dashboardLogic.createCalendarItem() below should be a key for a label such as "Due Date: " or "Accept Until: " // from dash_entity properties bundle for use in the dashboard list String type = cEvent.getType(); // Based on the event-type, we may be able to select a key for a label? String key = null; if (type == null) { key = "schedule.key2"; } else { key = scheduleEventTypeMap.get(type); if (key == null) { key = "schedule.key2"; } } // is this a repeating event? RecurrenceRule recurrenceRule = cEvent.getRecurrenceRule(); if (recurrenceRule == null) { // not a repeating event so create one calendar event CalendarItem calendarItem = dashboardLogic.createCalendarItem(cEvent.getDisplayName(), new Date(cEvent.getRange().firstTime().getTime()), key, cEventReference, context, sourceType, type, null, null); if (sitePublished) { dashboardLogic.createCalendarLinks(calendarItem); } } else { // this is a repeating event -- create a repeating calendar item String frequency = recurrenceRule.getFrequency(); int maxCount = recurrenceRule.getCount(); Date lastDate = null; if (recurrenceRule.getUntil() != null) { lastDate = new Date(recurrenceRule.getUntil().getTime()); } if (lastDate == null || lastDate.after(new Date())) { // there is no need to track past repeating schedule events // most likely those were brought in when old sites were duplicated or imported RepeatingCalendarItem repeatingCalendarItem = dashboardLogic .createRepeatingCalendarItem(cEvent.getDisplayName(), new Date(cEvent.getRange().firstTime().getTime()), lastDate, key, cEventReference, context, sourceType, frequency, maxCount); logger.debug(repeatingCalendarItem); } } } } else { // for now, let's log the error. // this event is posted for creation of a calendar as well as for creation of calendar events, so this is not necessarily an error. logger.debug(eventId + " is not processed for entityReference " + event.getResource()); } } } /** * Inner class: ScheduleRemoveEventProcessor */ public class ScheduleRemoveEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleRemoveEventProcessor.class); /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#getEventIdentifer() */ public String getEventIdentifer() { return SakaiProxy.EVENT_REMOVE_CALENDAR_EVENT; } /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#processEvent(org.sakaiproject.event.api.Event) */ public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("removing news links and news item for " + event.getResource()); } if (logger.isDebugEnabled()) { logger.debug("removing calendar links and news item for " + event.getResource()); } // remove all links and CalendarItem itself dashboardLogic.removeCalendarItems(event.getResource()); } } /** * Inner Class: ScheduleUpdateTitleEventProcessor */ public class ScheduleUpdateTitleEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateTitleEventProcessor.class); /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#getEventIdentifer() */ public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_TITLE; } /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#processEvent(org.sakaiproject.event.api.Event) */ public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("updating title of calendar item for " + event.getResource()); } Entity entity = sakaiProxy.getEntity(event.getResource()); if (entity != null && entity instanceof CalendarEvent) { // get the assignment entity and its current title CalendarEvent cEvent = (CalendarEvent) entity; String title = cEvent.getDisplayName(); // update news item title //dashboardLogic.reviseNewsItemTitle(cEvent.getReference(), title, null, null); String type = cEvent.getType(); // Based on the event-type, we may be able to select a key for a label? String key = null; if (type == null) { key = "schedule.key2"; } else { key = scheduleEventTypeMap.get(type); if (key == null) { key = "schedule.key2"; } } RecurrenceRule rule = cEvent.getRecurrenceRule(); if (rule == null) { // update calendar item title dashboardLogic.reviseCalendarItemsTitle(cEvent.getReference(), title); } else { // update repeating calendar item dashboardLogic.reviseRepeatingCalendarItemTitle(cEvent.getReference(), title); // update all instances of repeating calendar item dashboardLogic.reviseCalendarItemsTitle(cEvent.getReference(), title); } } if (logger.isDebugEnabled()) { logger.debug("removing news links and news item for " + event.getResource()); } } } /** * Inner Class: ScheduleUpdateTimeEventProcessor */ public class ScheduleUpdateTimeEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateTimeEventProcessor.class); /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#getEventIdentifer() */ public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_TIME; } /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#processEvent(org.sakaiproject.event.api.Event) */ public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("updating time of calendar item for " + event.getResource()); } String entityReference = event.getResource(); Entity entity = sakaiProxy.getEntity(entityReference); if (entity != null && entity instanceof CalendarEvent) { // get the calendar-event entity and its new time CalendarEvent cEvent = (CalendarEvent) entity; TimeRange range = cEvent.getRange(); String calendarTimeLabelKey = scheduleEventTypeMap.get(cEvent.getType()); Date newStartTime = new Date(range.firstTime().getTime()); //Date newEndTime = new Date(range.lastTime().getTime()); RecurrenceRule rule = cEvent.getRecurrenceRule(); if (rule != null) { // change times for the repeating calendar item and all instances // remove all instances and add new instances ResourceProperties props = cEvent.getProperties(); RepeatingCalendarItem item = dashboardLogic.getRepeatingCalendarItem(entityReference, calendarTimeLabelKey); // update the time of the repating item and each instance if (rule.getUntil() != null) { Date lastTime = new Date(rule.getUntil().getTime()); } dashboardLogic.reviseRepeatingCalendarItemTime(entityReference, newStartTime, null); // need to get each item in sequence and update its time Map<Integer, Date> dates = scheduleEntityType.generateRepeatingEventDates(entityReference, newStartTime, dashboardLogic.getRepeatingEventHorizon()); for (Map.Entry<Integer, Date> entry : dates.entrySet()) { if (logger.isDebugEnabled()) { String msg = entry.getKey().toString() + " ==> " + entry.getValue().toString(); CalendarItem oneItem = dashboardLogic.getCalendarItem(entityReference, calendarTimeLabelKey, entry.getKey()); if (oneItem != null) { msg += " -----> " + oneItem.getCalendarTime().toString(); } logger.debug(msg); } dashboardLogic.reviseCalendarItemTime(entityReference, calendarTimeLabelKey, entry.getKey(), entry.getValue()); } } else { // update calendar item title dashboardLogic.reviseCalendarItemsTime(entityReference, newStartTime); } } if (logger.isDebugEnabled()) { logger.debug("removing news links and news item for " + event.getResource()); } } } /** * Inner Class: ScheduleUpdateTypeEventProcessor */ public class ScheduleUpdateTypeEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateTypeEventProcessor.class); /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#getEventIdentifer() */ public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_TYPE; } /* (non-Javadoc) * @see org.sakaiproject.dash.listener.EventProcessor#processEvent(org.sakaiproject.event.api.Event) */ public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("updating type of calendar item for " + event.getResource()); } String[] parts = event.getResource().split("::"); if (parts.length > 2) { String entityReference = parts[0]; String oldType = parts[1]; String newType = parts[2]; String oldLabelKey = scheduleEventTypeMap.get(oldType); String newLabelKey = scheduleEventTypeMap.get(newType); Entity entity = sakaiProxy.getEntity(entityReference); if (entity != null && entity instanceof CalendarEvent) { // get the assignment entity and its new time CalendarEvent cEvent = (CalendarEvent) entity; if (logger.isDebugEnabled()) { logger.debug("removing news links and news item for " + entityReference); } if (cEvent.getRecurrenceRule() != null) { // update the label key for the repeating calendar item dashboardLogic.reviseRepeatingCalendarItemsLabelKey(entityReference, oldLabelKey, newLabelKey); // update the label key for all instances dashboardLogic.reviseCalendarItemsLabelKey(entityReference, oldLabelKey, newLabelKey); } else { // update calendar item title dashboardLogic.reviseCalendarItemsLabelKey(entityReference, oldLabelKey, newLabelKey); } } } } } public class ScheduleReviseEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleReviseEventProcessor.class); public String getEventIdentifer() { return SakaiProxy.EVENT_SCHEDULE_REVISE_EVENT; } public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("revising calendar item for " + event.getResource()); } } } public class ScheduleUpdateAccessEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateAccessEventProcessor.class); public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_ACCESS; } public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("revising calendar item for " + event.getResource()); } Entity entity = sakaiProxy.getEntity(event.getResource()); if (entity != null && entity instanceof AnnouncementMessage) { // get the calendar event entity CalendarEvent cEvent = (CalendarEvent) entity; String cReference = cEvent.getReference(); // update the calendar/news item links according to current announcement dashboardLogic.updateNewsLinks(cReference); dashboardLogic.updateCalendarLinks(cReference); } if (logger.isDebugEnabled()) { logger.debug("removing news links and news item for " + event.getResource()); } } } public class ScheduleUpdateFrequencyEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateFrequencyEventProcessor.class); public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_FREQUENCY; } public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } if (logger.isDebugEnabled()) { logger.debug("revising calendar item for " + event.getResource()); } String entityReference = event.getResource(); Entity entity = sakaiProxy.getEntity(entityReference); if (entity != null && entity instanceof CalendarEvent) { // get the calendar-event entity and its new time CalendarEvent cEvent = (CalendarEvent) entity; TimeRange range = cEvent.getRange(); String calendarTimeLabelKey = scheduleEventTypeMap.get(cEvent.getType()); Date newStartTime = new Date(range.firstTime().getTime()); //Date newEndTime = new Date(range.lastTime().getTime()); RecurrenceRule rule = cEvent.getRecurrenceRule(); if (rule == null) { // remove repeating-event object and all instances? // add single calendar item? } else { // update the repeating-event object RepeatingCalendarItem repeater = dashboardLogic.getRepeatingCalendarItem(entityReference, calendarTimeLabelKey); if (repeater == null) { // create repeating calendar item? } else { String frequency = rule.getFrequency(); if (frequency == null) { // what to do? logger.warn( "Error trying to revise frequency of repeating event: event.getRecurrenceRule().getFrequency() is null"); } else if (!frequency.equalsIgnoreCase(repeater.getFrequency())) { dashboardLogic.reviseRepeatingCalendarItemFrequency(entityReference, frequency); } } // need to get each item in sequence and update its time Map<Integer, Date> dates = scheduleEntityType.generateRepeatingEventDates(entityReference, newStartTime, dashboardLogic.getRepeatingEventHorizon()); Integer firstSequenceNumber = findSmallest(dates.keySet()); SortedSet<Integer> futureSequenceNumbers = dashboardLogic .getFutureSequnceNumbers(entityReference, calendarTimeLabelKey, firstSequenceNumber); for (Map.Entry<Integer, Date> entry : dates.entrySet()) { if (futureSequenceNumbers.contains(entry.getKey())) { // update each existing calendar-item dashboardLogic.reviseCalendarItemTime(entityReference, calendarTimeLabelKey, entry.getKey(), entry.getValue()); futureSequenceNumbers.remove(entry.getKey()); } else { // add new calendar-items as needed CalendarItem calendarItem = dashboardLogic.createCalendarItem(repeater.getTitle(), entry.getValue(), calendarTimeLabelKey, entityReference, repeater.getContext(), repeater.getSourceType(), repeater.getSubtype(), repeater, entry.getKey()); dashboardLogic.createCalendarLinks(calendarItem); } if (logger.isDebugEnabled()) { String msg = entry.getKey().toString() + " ==> " + entry.getValue().toString(); CalendarItem oneItem = dashboardLogic.getCalendarItem(entityReference, calendarTimeLabelKey, entry.getKey()); if (oneItem != null) { msg += " -----> " + oneItem.getCalendarTime().toString(); } logger.debug(msg); } } // futureSequenceNumbers now contains only the id's of existing calendar-items that need to be removed for (Integer seqNum : futureSequenceNumbers) { CalendarItem item = dashboardLogic.getCalendarItem(entityReference, calendarTimeLabelKey, seqNum); dashboardLogic.removeCalendarLinks(entityReference, calendarTimeLabelKey, seqNum.intValue()); dashboardLogic.removeCalendarItem(entityReference, calendarTimeLabelKey, seqNum); } } } } } public class ScheduleUpdateExcludedEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateExcludedEventProcessor.class); public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_EXCLUDED; } public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } // This is a case of a revision to one instance of a repeating event. If effect, // a new calendar-event entity has been created to represent the instance that // was excluded from the recurring event. String eventId = event.getEvent(); String eventContextString = event.getContext(); Entity entity = sakaiProxy.getEntity(event.getResource()); // handle add events if (entity != null && entity instanceof CalendarEvent) { CalendarEvent cEvent = (CalendarEvent) entity; String cEventReference = cEvent.getReference(); Context context = dashboardLogic.getContext(eventContextString); SourceType sourceType = dashboardLogic.getSourceType(IDENTIFIER); // Third parameter in dashboardLogic.createCalendarItem() below should be a key for a label such as "Due Date: " or "Accept Until: " // from dash_entity properties bundle for use in the dashboard list String type = cEvent.getType(); // Based on the event-type, we may be able to select a key for a label? String key = null; if (type == null) { key = "schedule.key2"; } else { key = scheduleEventTypeMap.get(type); if (key == null) { key = "schedule.key2"; } } // The schedule tool and/or service does not save a recurrence rule for the newly // separated calendar event, though the UI elements are presented to the user, // so we will assume this to be a non-repeating event. CalendarItem calendarItem = dashboardLogic.createCalendarItem(cEvent.getDisplayName(), new Date(cEvent.getRange().firstTime().getTime()), key, cEventReference, context, sourceType, type, null, null); dashboardLogic.createCalendarLinks(calendarItem); } else { // for now, let's log the error logger.info(eventId + " is not processed for entityReference " + event.getResource()); } } } public class ScheduleUpdateExclusionsEventProcessor implements EventProcessor { private Log logger = LogFactory.getLog(ScheduleUpdateExclusionsEventProcessor.class); public String getEventIdentifer() { return SakaiProxy.EVENT_MODIFY_CALENDAR_EVENT_EXCLUSIONS; } public void processEvent(Event event) { if (logger.isDebugEnabled()) { logger.debug("\n\n\n=============================================================\n" + event + "\n=============================================================\n\n\n"); } // This is a case of a revision to one instance of a repeating event, which is // then no longer an instance of the repeating event. This event processor handles // the change to the repeating event: exclusion of one instance from the sequence. // TODO: remove the calendar item if it has already been created. // sample entityReference /calendar/event/f971f216-625c-4e5e-9609-05313f836ae2/main/!20111125131500000]20111125141500000!49!c4c1f3f5-c331-4c5e-8afe-07168ff0cc72 String entityReference = null; int sequenceNumber = -1; if (event != null && event.getResource() != null) { String[] parts = event.getResource().split("/"); if (parts.length > 5) { StringBuilder buf = new StringBuilder(); buf.append("/"); // 'calendar' buf.append(parts[1]); buf.append("/"); // 'event' buf.append(parts[2]); buf.append("/"); // site-id buf.append(parts[3]); buf.append("/"); // 'main' buf.append(parts[4]); buf.append("/"); String[] subparts = parts[5].split("!"); if (subparts.length > 3) { // event-id buf.append(subparts[3]); sequenceNumber = Integer.parseInt(subparts[2]); } entityReference = buf.toString(); } if (logger.isDebugEnabled()) { logger.info("processEvent() " + entityReference + " " + sequenceNumber); } if (entityReference != null && sequenceNumber >= -1) { String calendarTimeLabelKey = null; CalendarItem calendarItem = dashboardLogic.getCalendarItem(entityReference, calendarTimeLabelKey, sequenceNumber); dashboardLogic.removeCalendarItem(entityReference, calendarTimeLabelKey, sequenceNumber); } } } } public Integer findSmallest(Set<Integer> numbers) { SortedSet<Integer> set = new TreeSet<Integer>(numbers); return set.first(); } public Integer findLargest(Set<Integer> numbers) { SortedSet<Integer> set = new TreeSet<Integer>(numbers); return set.last(); } }