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