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.ZoneId; 019import java.time.ZonedDateTime; 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; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.core.observation.Event; 031import org.ametys.core.observation.ObservationManager; 032import org.ametys.core.user.CurrentUserProvider; 033import org.ametys.core.user.UserIdentity; 034import org.ametys.core.util.DateUtils; 035import org.ametys.plugins.explorer.ExplorerNode; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 038import org.ametys.plugins.workflow.store.AmetysObjectWorkflowStore; 039import org.ametys.plugins.workspaces.calendars.CalendarDAO; 040import org.ametys.plugins.workspaces.calendars.ModifiableCalendar; 041import org.ametys.plugins.workspaces.calendars.ObservationConstants; 042import org.ametys.plugins.workspaces.calendars.events.CalendarEvent; 043import org.ametys.plugins.workspaces.calendars.events.CalendarEventDAO; 044import org.ametys.plugins.workspaces.calendars.events.CalendarEventJSONHelper; 045import org.ametys.plugins.workspaces.calendars.events.ModifiableCalendarEvent; 046import org.ametys.plugins.workspaces.calendars.jcr.JCRCalendarEventFactory; 047import org.ametys.plugins.workspaces.tags.ProjectTagsDAO; 048import org.ametys.plugins.workspaces.workflow.AbstractNodeWorkflowComponent; 049 050import com.opensymphony.module.propertyset.PropertySet; 051import com.opensymphony.workflow.FunctionProvider; 052import com.opensymphony.workflow.StoreException; 053import com.opensymphony.workflow.WorkflowException; 054import com.opensymphony.workflow.spi.WorkflowEntry; 055import com.opensymphony.workflow.spi.WorkflowStore; 056 057/** 058 * Action for adding a calendar event 059 */ 060public class AddEventFunction extends AbstractNodeWorkflowComponent implements FunctionProvider 061{ 062 /** The Ametys object resolver */ 063 protected AmetysObjectResolver _resolver; 064 065 /** Observer manager. */ 066 protected ObservationManager _observationManager; 067 068 /** The current user provider. */ 069 protected CurrentUserProvider _currentUserProvider; 070 071 /** The project tags DAO */ 072 protected ProjectTagsDAO _projectTagsDAO; 073 074 /** Calendar manager for workspaces */ 075 protected CalendarDAO _calendarDAO; 076 077 /** Calendar event manager for workspaces */ 078 protected CalendarEventDAO _calendarEventDAO; 079 080 /** The tasks list JSON helper */ 081 protected CalendarEventJSONHelper _calendarEventJSONHelper; 082 083 @Override 084 public void service(ServiceManager smanager) throws ServiceException 085 { 086 super.service(smanager); 087 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 088 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 089 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 090 _projectTagsDAO = (ProjectTagsDAO) smanager.lookup(ProjectTagsDAO.ROLE); 091 _calendarDAO = (CalendarDAO) smanager.lookup(CalendarDAO.ROLE); 092 _calendarEventDAO = (CalendarEventDAO) smanager.lookup(CalendarEventDAO.ROLE); 093 _calendarEventJSONHelper = (CalendarEventJSONHelper) smanager.lookup(CalendarEventJSONHelper.ROLE); 094 } 095 096 @Override 097 public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException 098 { 099 @SuppressWarnings("unchecked") 100 Map<String, Object> jsParameters = (Map<String, Object>) transientVars.get("parameters"); 101 if (jsParameters == null) 102 { 103 throw new WorkflowException("Missing JS parameters"); 104 } 105 106 ExplorerNode object = getExplorerNode(transientVars); 107 108 String parentId = (String) jsParameters.get("parentId"); 109 110 UserIdentity user = getUser(transientVars); 111 String selectedNode = (String) jsParameters.get("selectedNode"); 112 113 @SuppressWarnings("unchecked") 114 Map<String, Object> result = (Map<String, Object>) transientVars.get("result"); 115 116 // FIXME EXPLORER-441 Remove "renameIfExists" on calendar events workflow 117 // boolean renameIfExists = (Boolean) jsParameters.get("renameIfExists"); 118 assert parentId != null; 119 120 if (!(object instanceof ModifiableCalendar)) 121 { 122 throw new IllegalClassException(ModifiableCalendar.class, object.getClass()); 123 } 124 125 ModifiableCalendar modifiableCalendar = (ModifiableCalendar) object; 126 127 // Create new event 128 ModifiableCalendarEvent event = ((ModifiableTraversableAmetysObject) modifiableCalendar).createChild("ametys:calendar-event", JCRCalendarEventFactory.CALENDAR_EVENT_NODETYPE); 129 130 ZonedDateTime now = ZonedDateTime.now(); 131 event.setCreationDate(now); 132 event.setLastModified(now); 133 event.setCreator(user); 134 135 // Set event's properties 136 _setEventData(event, transientVars, jsParameters); 137 138 // Bind workflow store 139 _initializeWorkflow(event, transientVars, jsParameters); 140 141 modifiableCalendar.saveChanges(); 142 143 transientVars.put("eventId", event.getId()); 144 145 result.put("id", event.getId()); 146 result.put("parentId", selectedNode == null ? parentId : selectedNode); 147 result.put("event", _calendarEventJSONHelper.eventAsJsonWithOccurrences(event, false, event.getStartDate(), event.getEndDate())); 148 // Notify listeners 149 _notifyListeners(event); 150 } 151 152 /** 153 * Initialize the workflow store 154 * @param event The event 155 * @param transientVars The transient variables 156 * @param jsParameters The JS parameters 157 * @throws WorkflowException if an error occurred 158 */ 159 protected void _initializeWorkflow (ModifiableCalendarEvent event, Map transientVars, Map<String, Object> jsParameters) throws WorkflowException 160 { 161 long workflowId = ((WorkflowEntry) transientVars.get("entry")).getId(); 162 163 try 164 { 165 event.setWorkflowId(workflowId); 166 WorkflowStore workflowStore = (WorkflowStore) transientVars.get("store"); 167 168 if (workflowStore instanceof AmetysObjectWorkflowStore) 169 { 170 AmetysObjectWorkflowStore ametysObjectWorkflowStore = (AmetysObjectWorkflowStore) workflowStore; 171 ametysObjectWorkflowStore.bindAmetysObject(event); 172 } 173 } 174 catch (StoreException e) 175 { 176 throw new WorkflowException("Unable to link the workflow to the content", e); 177 } 178 } 179 180 /** 181 * Set the event data 182 * @param event The event 183 * @param transientVars The transient variables 184 * @param jsParameters The JS parameters 185 * @throws WorkflowException if an error occurred 186 */ 187 protected void _setEventData (ModifiableCalendarEvent event, Map transientVars, Map<String, Object> jsParameters) throws WorkflowException 188 { 189 @SuppressWarnings("unchecked") 190 Map<String, Object> result = (Map<String, Object>) transientVars.get("result"); 191 String title = (String) jsParameters.get("title"); 192 String desc = (String) jsParameters.get("description"); 193 String startDateAsString = (String) jsParameters.get("startDate"); 194 195 String zoneId = (String) jsParameters.get("zoneId"); 196 ZoneId zone = ZoneId.of(zoneId); 197 event.setZone(zone); 198 199 ZonedDateTime startDate = DateUtils.parseZonedDateTime(startDateAsString).withZoneSameInstant(zone); 200 String endDateAsString = (String) jsParameters.get("endDate"); 201 ZonedDateTime endDate = DateUtils.parseZonedDateTime(endDateAsString).withZoneSameInstant(zone); 202 203 Object rawFullDay = jsParameters.get("fullDay"); 204 Boolean fullDay = rawFullDay instanceof Boolean ? (Boolean) rawFullDay : Boolean.parseBoolean((String) rawFullDay); 205 206 String recurrenceType = (String) jsParameters.get("recurrenceType"); 207 String untilDateAsString = (String) jsParameters.get("untilDate"); 208 ZonedDateTime untilDate = DateUtils.parseZonedDateTime(untilDateAsString); 209 210 UserIdentity user = getUser(transientVars); 211 212 event.setTitle(title); 213 event.setDescription(desc); 214 event.setFullDay(fullDay); 215 216 event.setStartDate(startDate); 217 event.setLastContributor(user); 218 event.setLastModified(ZonedDateTime.now()); 219 220 event.setRecurrenceType(recurrenceType); 221 if (untilDate != null) 222 { 223 event.setRepeatUntil(untilDate); 224 } 225 226 event.setEndDate(endDate); 227 228 String location = (String) jsParameters.get("location"); 229 if (StringUtils.isNotEmpty(location)) 230 { 231 event.setLocation(location); 232 } 233 234 @SuppressWarnings("unchecked") 235 List<Object> tags = (List<Object>) jsParameters.getOrDefault("tags", new ArrayList<>()); 236 List<String> createdTags = new ArrayList<>(); 237 List<Map<String, Object>> createdTagsJson = new ArrayList<>(); 238 // Tag new tags 239 for (Object tag : tags) 240 { 241 // Tag doesn't exist so create the tag 242 if (tag instanceof Map) 243 { 244 @SuppressWarnings("unchecked") 245 String tagText = (String) ((Map<String, Object>) tag).get("text"); 246 List<Map<String, Object>> newTags = _projectTagsDAO.addTags(new String[] {tagText}); 247 String newTag = (String) newTags.get(0).get("name"); 248 event.tag(newTag); 249 createdTags.add(newTag); 250 createdTagsJson.addAll(newTags); 251 } 252 else 253 { 254 event.tag((String) tag); 255 } 256 } 257 // Untag unused tags 258 for (String tag : event.getTags()) 259 { 260 if (!tags.contains(tag) && !createdTags.contains(tag)) 261 { 262 event.untag(tag); 263 } 264 } 265 266 @SuppressWarnings("unchecked") 267 List<String> resources = (List<String>) jsParameters.getOrDefault("resourceIds", new ArrayList<>()); 268 event.setResources(resources); 269 result.put("newTags", createdTagsJson); 270 } 271 272 /** 273 * Notify listeners that the event has been created 274 * @param event The created event 275 */ 276 protected void _notifyListeners (CalendarEvent event) 277 { 278 Map<String, Object> eventParams = new HashMap<>(); 279 eventParams.put(ObservationConstants.ARGS_CALENDAR, event.getParent()); 280 eventParams.put(ObservationConstants.ARGS_CALENDAR_EVENT, event); 281 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_ID, event.getId()); 282 eventParams.put(org.ametys.plugins.explorer.ObservationConstants.ARGS_PARENT_ID, event.getParent().getId()); 283 284 _observationManager.notify(new Event(ObservationConstants.EVENT_CALENDAR_EVENT_CREATED, _currentUserProvider.getUser(), eventParams)); 285 } 286}