001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.workspaces.calendars.workflow; 017 018import java.time.ZonedDateTime; 019import java.time.temporal.ChronoUnit; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.lang.IllegalClassException; 028 029import org.ametys.core.observation.Event; 030import org.ametys.core.observation.ObservationManager; 031import org.ametys.core.user.CurrentUserProvider; 032import org.ametys.core.util.DateUtils; 033import org.ametys.plugins.messagingconnector.EventRecurrenceTypeEnum; 034import org.ametys.plugins.repository.AmetysObject; 035import org.ametys.plugins.repository.AmetysObjectResolver; 036import org.ametys.plugins.repository.AmetysRepositoryException; 037import org.ametys.plugins.workflow.support.WorkflowProvider; 038import org.ametys.plugins.workspaces.calendars.Calendar; 039import org.ametys.plugins.workspaces.calendars.ModifiableCalendar; 040import org.ametys.plugins.workspaces.calendars.ObservationConstants; 041import org.ametys.plugins.workspaces.calendars.events.CalendarEvent; 042import org.ametys.plugins.workspaces.calendars.events.ModifiableCalendarEvent; 043import org.ametys.plugins.workspaces.workflow.AbstractNodeWorkflowComponent; 044 045import com.opensymphony.module.propertyset.PropertySet; 046import com.opensymphony.workflow.Workflow; 047import com.opensymphony.workflow.WorkflowException; 048import com.opensymphony.workflow.spi.WorkflowEntry; 049 050/** 051 * Action for editing a calendar event 052 */ 053public class EditEventFunction extends AddEventFunction 054{ 055 /** Workflow provider */ 056 protected WorkflowProvider _workflowProvider; 057 058 @Override 059 public void service(ServiceManager smanager) throws ServiceException 060 { 061 super.service(smanager); 062 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 063 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 064 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 065 _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE); 066 } 067 068 @Override 069 public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException 070 { 071 @SuppressWarnings("unchecked") 072 Map<String, Object> jsParameters = (Map<String, Object>) transientVars.get("parameters"); 073 if (jsParameters == null) 074 { 075 throw new WorkflowException("no parameters defined"); 076 } 077 078 String eventId = (String) jsParameters.get("id"); 079 CalendarEvent event = _resolver.resolveById(eventId); 080 081 String parentId = (String) jsParameters.get("parentId"); 082 Calendar calendar = _resolver.resolveById(parentId); 083 084 if (!(calendar instanceof ModifiableCalendar)) 085 { 086 throw new IllegalClassException(ModifiableCalendar.class, calendar.getClass()); 087 } 088 089 if (!(event instanceof ModifiableCalendarEvent)) 090 { 091 throw new IllegalClassException(ModifiableCalendarEvent.class, event.getClass()); 092 } 093 094 ModifiableCalendarEvent mEvent = (ModifiableCalendarEvent) event; 095 ModifiableCalendar mCalendar = (ModifiableCalendar) calendar; 096 097 String choice = (String) jsParameters.get("choice"); 098 099 @SuppressWarnings("unchecked") 100 Map<String, Object> result = (Map<String, Object>) transientVars.get("result"); 101 102 if ("unit".equals(choice)) 103 { 104 // Create a new event from the edited occurrence 105 _createEventFromOccurrence(mEvent, mCalendar, transientVars, jsParameters); 106 107 // Exclude this occurrence from the initial event 108 String occurrenceDateAsString = (String) jsParameters.get("occurrenceDate"); 109 ZonedDateTime occurrenceDate = DateUtils.parseZonedDateTime(occurrenceDateAsString); 110 _excludeOccurrence(mEvent, occurrenceDate); 111 112 mEvent.saveChanges(); 113 114 _notifyListeners(mEvent); 115 } 116 else 117 { 118 // FIXME EXPLORER-441 Remove "renameIfExists" on calendar events workflow 119 // boolean renameIfExists = (Boolean) jsParameters.get("renameIfExists"); 120 assert parentId != null; 121 122 AmetysObject object = _resolver.resolveById(parentId); 123 if (!(object instanceof ModifiableCalendar)) 124 { 125 throw new IllegalClassException(ModifiableCalendar.class, object.getClass()); 126 } 127 128 long workflowId = ((WorkflowEntry) transientVars.get("entry")).getId(); 129 event.setWorkflowId(workflowId); 130 131 // Compute the event's dates from the dates of the modified occurrence 132 ZonedDateTime[] dates = _computeEventDates(event, jsParameters); 133 134 _setEventData(mEvent, transientVars, jsParameters); 135 136 mEvent.setStartDate(dates[0]); 137 mEvent.setEndDate(dates[1]); 138 139 mEvent.saveChanges(); 140 141 result.put("id", event.getId()); 142 result.put("parentId", parentId); 143 result.put("event", _calendarEventJSONHelper.eventAsJsonWithOccurrences(event, false, event.getStartDate(), event.getEndDate())); 144 145 // Notify listeners 146 _notifyListeners(mEvent); 147 } 148 } 149 150 /** 151 * Create a new event from the occurrence of a event 152 * @param initialEvent The initial event 153 * @param parentCalendar The parent calendar 154 * @param transientVars The transient variable 155 * @param jsParameters The JS parameters 156 */ 157 protected void _createEventFromOccurrence (CalendarEvent initialEvent, Calendar parentCalendar, Map transientVars, Map<String, Object> jsParameters) 158 { 159 @SuppressWarnings("unchecked") 160 Map<String, Object> result = (Map<String, Object>) transientVars.get("result"); 161 162 // Computed workflow parameters to create a new event 163 if (!jsParameters.containsKey("title")) 164 { 165 jsParameters.put("title", initialEvent.getTitle()); 166 jsParameters.put("desc", initialEvent.getDescription()); 167 jsParameters.put("fullDay", initialEvent.getFullDay()); 168 } 169 jsParameters.put("recurrenceType", EventRecurrenceTypeEnum.NEVER.toString()); 170 jsParameters.put("untilDate", null); 171 172 Map<String, Object> inputs = new HashMap<>(); 173 inputs.put("parameters", jsParameters); 174 inputs.put(AbstractNodeWorkflowComponent.EXPLORERNODE_KEY, parentCalendar); 175 inputs.put("result", result); 176 177 String workflowName = parentCalendar.getWorkflowName(); 178 try 179 { 180 // Create a new event from the edited occurrence 181 Workflow workflow = _workflowProvider.getAmetysObjectWorkflow(); 182 workflow.initialize(workflowName, 1, inputs); 183 } 184 catch (WorkflowException e) 185 { 186 throw new AmetysRepositoryException("Unable to create a new event with workflow '" + workflowName + "' and action 1'", e); 187 } 188 } 189 190 /** 191 * Exclude a occurrence of a event 192 * @param event The event 193 * @param occurrenceDate The date to exclude 194 */ 195 protected void _excludeOccurrence (ModifiableCalendarEvent event, ZonedDateTime occurrenceDate) 196 { 197 List<ZonedDateTime> excludedOccurrences = new ArrayList<>(event.getExcludedOccurences()); 198 199 excludedOccurrences.add(occurrenceDate.truncatedTo(ChronoUnit.DAYS)); 200 201 // Exclude the occurrence 202 event.setExcludedOccurrences(excludedOccurrences); 203 } 204 205 /** 206 * Compute the start date and end date of the event from the edited occurrence 207 * @param event The event 208 * @param jsParameters The JS parameters 209 * @return an array with computed start and end dates 210 */ 211 protected ZonedDateTime[] _computeEventDates (CalendarEvent event, Map<String, Object> jsParameters) 212 { 213 // Date of the edited occurrence before edition 214 String occurrenceDateAsString = (String) jsParameters.get("occurrenceDate"); 215 ZonedDateTime occurrenceDate = DateUtils.parseZonedDateTime(occurrenceDateAsString); 216 217 // Dates of the event before edition 218 long oldDiff = ChronoUnit.SECONDS.between(event.getStartDate(), event.getEndDate()); 219 220 ZonedDateTime occurrenceEndDate = occurrenceDate.plusSeconds(oldDiff); 221 222 String startDateAsString = (String) jsParameters.get("startDate"); 223 ZonedDateTime startDate = DateUtils.parseZonedDateTime(startDateAsString); 224 String endDateAsString = (String) jsParameters.get("endDate"); 225 ZonedDateTime endDate = DateUtils.parseZonedDateTime(endDateAsString); 226 227 long startDiff = ChronoUnit.SECONDS.between(occurrenceDate, startDate); 228 long endDiff = ChronoUnit.SECONDS.between(occurrenceEndDate, endDate); 229 230 // Compute the new start and end date from the edited occurrence 231 return new ZonedDateTime[] {event.getStartDate().plusSeconds(startDiff), event.getEndDate().plusSeconds(endDiff)}; 232 } 233 234 235 /** 236 * Notify listeners that the event has been updated 237 * @param event The updated event 238 */ 239 @Override 240 protected void _notifyListeners (CalendarEvent event) 241 { 242 Map<String, Object> eventParams = new HashMap<>(); 243 eventParams.put(ObservationConstants.ARGS_CALENDAR, event.getParent()); 244 eventParams.put(ObservationConstants.ARGS_CALENDAR_EVENT, event); 245 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, event.getId()); 246 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_PARENT_ID, event.getParent().getId()); 247 248 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED, _currentUserProvider.getUser(), eventParams)); 249 } 250}