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.explorer.calendars.jcr; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Calendar; 021import java.util.Date; 022import java.util.GregorianCalendar; 023import java.util.List; 024 025import javax.jcr.Node; 026import javax.jcr.RepositoryException; 027 028import org.ametys.core.user.UserIdentity; 029import org.ametys.plugins.explorer.calendars.CalendarEvent; 030import org.ametys.plugins.explorer.calendars.EventRecurrenceTypeEnum; 031import org.ametys.plugins.explorer.calendars.ModifiableCalendarEvent; 032import org.ametys.plugins.explorer.calendars.helper.RecurrentEventHelper; 033import org.ametys.plugins.repository.AmetysObject; 034import org.ametys.plugins.repository.AmetysRepositoryException; 035import org.ametys.plugins.repository.RepositoryConstants; 036import org.ametys.plugins.repository.jcr.DefaultAmetysObject; 037import org.ametys.plugins.repository.metadata.CompositeMetadata; 038import org.ametys.plugins.repository.metadata.UnknownMetadataException; 039 040/** 041 * Default implementation of an {@link CalendarEvent}, backed by a JCR node.<br> 042 */ 043public class JCRCalendarEvent extends DefaultAmetysObject<JCRCalendarEventFactory> implements ModifiableCalendarEvent 044{ 045 /** Metadata's name for author's event* */ 046 public static final String METADATA_CREATOR = "creator"; 047 048 /** Metadata's name for lastModified's event* */ 049 public static final String METADATA_CREATION = "creationDate"; 050 051 /** Metadata's name for last contributor's event* */ 052 public static final String METADATA_CONTRIBUTOR = "contributor"; 053 054 /** Metadata's name for lastModified's event* */ 055 public static final String METADATA_MODIFIED = "lastModified"; 056 057 /** Metadata's name for title's event* */ 058 public static final String METADATA_TITLE = "title"; 059 060 /** Metadata's name for description's event* */ 061 public static final String METADATA_DESC = "description"; 062 063 /** Metadata's name for keywords' event* */ 064 public static final String METADATA_KEYWORDS = "keywords"; 065 066 /** Metadata's name for location's event* */ 067 public static final String METADATA_LOCATION = "location"; 068 069 /** Metadata's name for startDate's event* */ 070 public static final String METADATA_START_DATE = "startDate"; 071 072 /** Metadata's name for endDate's event* */ 073 public static final String METADATA_END_DATE = "endDate"; 074 075 /** Metadata's name for last validated date's event* */ 076 public static final String METADATA_VALIDATED = "lastValidated"; 077 078 /** Metadata's name for last validator's event* */ 079 public static final String METADATA_VALIDATOR = "validator"; 080 081 /** Metadata's name for fullDay's event* */ 082 public static final String METADATA_FULL_DAY = "fullDay"; 083 084 /** Metadata's name for fullDay's event* */ 085 public static final String METADATA_RECURRENCE_TYPE = "recurrenceType"; 086 087 /** Metadata's name for fullDay's event* */ 088 public static final String METADATA_UNTIL_DATE = "untilDate"; 089 090 /** Metadata's name for fullDay's event* */ 091 public static final String METADATA_EXCLUDED_DATE = "excludedDate"; 092 093 /** Property's name for workflow id */ 094 public static final String PROPERTY_WORKFLOW_ID = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":workflowId"; 095 096 /** 097 * Creates an {@link JCRCalendarEvent}. 098 * @param node the node backing this {@link AmetysObject} 099 * @param parentPath the parentPath in the Ametys hierarchy 100 * @param factory the DefaultAmetysObjectFactory which created the AmetysObject 101 */ 102 public JCRCalendarEvent(Node node, String parentPath, JCRCalendarEventFactory factory) 103 { 104 super(node, parentPath, factory); 105 } 106 107 @Override 108 public String getTitle() 109 { 110 return getMetadataHolder().getString(METADATA_TITLE); 111 } 112 113 @Override 114 public String getDescription() 115 { 116 return getMetadataHolder().getString(METADATA_DESC); 117 } 118 119 @Override 120 public String getLocation() 121 { 122 return getMetadataHolder().getString(METADATA_LOCATION, null); 123 } 124 125 @Override 126 public String[] getKeywords() throws AmetysRepositoryException 127 { 128 return getMetadataHolder().getStringArray(METADATA_KEYWORDS, new String[0]); 129 } 130 131 @Override 132 public Date getStartDate() 133 { 134 return getMetadataHolder().getDate(METADATA_START_DATE); 135 } 136 137 @Override 138 public Date getEndDate() 139 { 140 return getMetadataHolder().getDate(METADATA_END_DATE); 141 } 142 143 @Override 144 public Boolean getFullDay() 145 { 146 return getMetadataHolder().getBoolean(METADATA_FULL_DAY); 147 } 148 149 @Override 150 public UserIdentity getCreator() 151 { 152 CompositeMetadata creatorMetadata = getMetadataHolder().getCompositeMetadata(METADATA_CREATOR); 153 return new UserIdentity(creatorMetadata.getString("login"), creatorMetadata.getString("population")); 154 } 155 156 @Override 157 public Date getCreationDate() 158 { 159 return getMetadataHolder().getDate(METADATA_CREATION); 160 } 161 162 @Override 163 public UserIdentity getLastContributor() 164 { 165 CompositeMetadata contributorMetadata = getMetadataHolder().getCompositeMetadata(METADATA_CONTRIBUTOR); 166 return new UserIdentity(contributorMetadata.getString("login"), contributorMetadata.getString("population")); 167 } 168 169 @Override 170 public Date getLastModified() 171 { 172 return getMetadataHolder().getDate(METADATA_MODIFIED); 173 } 174 175 @Override 176 public UserIdentity getLastValidator() throws UnknownMetadataException, AmetysRepositoryException 177 { 178 if (getMetadataHolder().hasMetadata(METADATA_VALIDATOR)) 179 { 180 CompositeMetadata validatorMetadata = getMetadataHolder().getCompositeMetadata(METADATA_VALIDATOR); 181 return new UserIdentity(validatorMetadata.getString("login"), validatorMetadata.getString("population")); 182 } 183 184 return null; 185 } 186 187 @Override 188 public Date getLastValidated() throws UnknownMetadataException, AmetysRepositoryException 189 { 190 if (getMetadataHolder().hasMetadata(METADATA_VALIDATED)) 191 { 192 return getMetadataHolder().getDate(METADATA_VALIDATED); 193 } 194 195 return null; 196 } 197 198 @Override 199 public EventRecurrenceTypeEnum getRecurrenceType() 200 { 201 EventRecurrenceTypeEnum recurrenceEnum = EventRecurrenceTypeEnum.NEVER; 202 if (getMetadataHolder().hasMetadata(METADATA_RECURRENCE_TYPE)) 203 { 204 String recurrenceType = getMetadataHolder().getString(METADATA_RECURRENCE_TYPE); 205 try 206 { 207 recurrenceEnum = EventRecurrenceTypeEnum.valueOf(recurrenceType); 208 } 209 catch (Exception e) 210 { 211 // ignore 212 } 213 } 214 215 return recurrenceEnum; 216 } 217 218 @Override 219 public Boolean isRecurrent() 220 { 221 return !getRecurrenceType().equals(EventRecurrenceTypeEnum.NEVER); 222 } 223 224 @Override 225 public Date getRepeatUntil() 226 { 227 if (getMetadataHolder().hasMetadata(METADATA_UNTIL_DATE)) 228 { 229 return getMetadataHolder().getDate(METADATA_UNTIL_DATE); 230 } 231 232 return null; 233 } 234 235 @Override 236 public List<Date> getExcludedOccurences() 237 { 238 List<Date> excludedOccurences = new ArrayList<>(); 239 if (getMetadataHolder().hasMetadata(METADATA_EXCLUDED_DATE)) 240 { 241 excludedOccurences = Arrays.asList(getMetadataHolder().getDateArray(METADATA_EXCLUDED_DATE)); 242 } 243 return excludedOccurences; 244 } 245 246 @Override 247 public void setTitle(String title) 248 { 249 getMetadataHolder().setMetadata(METADATA_TITLE, title); 250 } 251 252 @Override 253 public void setDescription(String desc) 254 { 255 getMetadataHolder().setMetadata(METADATA_DESC, desc); 256 } 257 258 @Override 259 public void setLocation(String location) 260 { 261 getMetadataHolder().setMetadata(METADATA_LOCATION, location); 262 } 263 264 @Override 265 public void setKeywords(String[] keywords) 266 { 267 getMetadataHolder().setMetadata(METADATA_KEYWORDS, keywords); 268 } 269 270 @Override 271 public void setStartDate(Date startDate) 272 { 273 getMetadataHolder().setMetadata(METADATA_START_DATE, startDate); 274 } 275 276 @Override 277 public void setEndDate(Date endDate) 278 { 279 getMetadataHolder().setMetadata(METADATA_END_DATE, endDate); 280 } 281 282 @Override 283 public void setFullDay(Boolean fullDay) 284 { 285 getMetadataHolder().setMetadata(METADATA_FULL_DAY, fullDay); 286 } 287 288 @Override 289 public void setCreator(UserIdentity user) throws UnknownMetadataException, AmetysRepositoryException 290 { 291 try 292 { 293 Node creatorNode = null; 294 if (getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR)) 295 { 296 creatorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR); 297 } 298 else 299 { 300 creatorNode = getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CREATOR, RepositoryConstants.USER_NODETYPE); 301 } 302 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", user.getLogin()); 303 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", user.getPopulationId()); 304 } 305 catch (RepositoryException e) 306 { 307 throw new AmetysRepositoryException("Error setting the creator property.", e); 308 } 309 } 310 311 @Override 312 public void setCreationDate(Date date) throws UnknownMetadataException, AmetysRepositoryException 313 { 314 getMetadataHolder().setMetadata(METADATA_CREATION, date); 315 } 316 317 @Override 318 public void setLastContributor(UserIdentity user) throws UnknownMetadataException, AmetysRepositoryException 319 { 320 try 321 { 322 Node creatorNode = null; 323 if (getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR)) 324 { 325 creatorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR); 326 } 327 else 328 { 329 creatorNode = getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_CONTRIBUTOR, RepositoryConstants.USER_NODETYPE); 330 } 331 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", user.getLogin()); 332 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", user.getPopulationId()); 333 } 334 catch (RepositoryException e) 335 { 336 throw new AmetysRepositoryException("Error setting the contributor property.", e); 337 } 338 } 339 340 @Override 341 public void setLastModified(Date date) throws UnknownMetadataException, AmetysRepositoryException 342 { 343 getMetadataHolder().setMetadata(METADATA_MODIFIED, date); 344 } 345 346 @Override 347 public void setLastValidator(UserIdentity user) throws UnknownMetadataException, AmetysRepositoryException 348 { 349 try 350 { 351 Node creatorNode = null; 352 if (getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_VALIDATOR)) 353 { 354 creatorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_VALIDATOR); 355 } 356 else 357 { 358 creatorNode = getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ':' + METADATA_VALIDATOR, RepositoryConstants.USER_NODETYPE); 359 } 360 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", user.getLogin()); 361 creatorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", user.getPopulationId()); 362 } 363 catch (RepositoryException e) 364 { 365 throw new AmetysRepositoryException("Error setting the validator property.", e); 366 } 367 } 368 369 @Override 370 public void setLastValidated(Date date) throws UnknownMetadataException, AmetysRepositoryException 371 { 372 getMetadataHolder().setMetadata(METADATA_VALIDATED, date); 373 } 374 375 @Override 376 public void setRecurrenceType(String recurrenceType) 377 { 378 getMetadataHolder().setMetadata(METADATA_RECURRENCE_TYPE, recurrenceType); 379 } 380 381 @Override 382 public void setRepeatUntil(Date untilDate) 383 { 384 if (untilDate == null) 385 { 386 if (getMetadataHolder().hasMetadata(METADATA_UNTIL_DATE)) 387 { 388 getMetadataHolder().removeMetadata(METADATA_UNTIL_DATE); 389 } 390 } 391 else 392 { 393 getMetadataHolder().setMetadata(METADATA_UNTIL_DATE, untilDate); 394 } 395 } 396 397 @Override 398 public void setExcludedOccurrences(List<Date> excludedOccurrences) 399 { 400 getMetadataHolder().setMetadata(METADATA_EXCLUDED_DATE, excludedOccurrences.toArray(new Date[excludedOccurrences.size()])); 401 } 402 403 @Override 404 public List<Date> getOccurrences(Date startDate, Date endDate) 405 { 406 Date untilDate = getRepeatUntil(); 407 408 // until date must be included in list of occurrences. 409 // For that purpose, until date is used here as a threshold value that 410 // must be set to the start of the next day 411 if (untilDate != null) 412 { 413 Calendar cal = Calendar.getInstance(); 414 cal.setTime(untilDate); 415 cal.add(Calendar.DATE, 1); 416 cal.set(java.util.Calendar.HOUR_OF_DAY, 0); 417 cal.set(java.util.Calendar.MINUTE, 0); 418 cal.set(java.util.Calendar.SECOND, 0); 419 cal.set(java.util.Calendar.MILLISECOND, 0); 420 untilDate = cal.getTime(); 421 } 422 423 List<Date> occurences = new ArrayList<>(); 424 Date firstDate = getFirstOccurrence(startDate); 425 if (firstDate == null || (untilDate != null && !firstDate.before(untilDate))) 426 { 427 return occurences; 428 } 429 430 List<Date> excludedOccurences = getExcludedOccurences(); 431 432 GregorianCalendar firstDateCalendar = new GregorianCalendar(); 433 firstDateCalendar.setTime(firstDate); 434 firstDateCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); 435 firstDateCalendar.set(java.util.Calendar.MINUTE, 0); 436 firstDateCalendar.set(java.util.Calendar.SECOND, 0); 437 firstDateCalendar.set(java.util.Calendar.MILLISECOND, 0); 438 439 if (!excludedOccurences.contains(firstDateCalendar.getTime())) 440 { 441 occurences.add(firstDate); 442 } 443 444 Date nextDate = getNextOccurrence(firstDate); 445 while (nextDate != null && nextDate.before(endDate) && (untilDate == null || untilDate.after(nextDate))) 446 { 447 GregorianCalendar nextDateCalendar = new GregorianCalendar(); 448 nextDateCalendar.setTime(nextDate); 449 nextDateCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); 450 nextDateCalendar.set(java.util.Calendar.MINUTE, 0); 451 nextDateCalendar.set(java.util.Calendar.SECOND, 0); 452 nextDateCalendar.set(java.util.Calendar.MILLISECOND, 0); 453 454 if (!excludedOccurences.contains(nextDateCalendar.getTime())) 455 { 456 occurences.add(nextDate); 457 } 458 nextDate = getNextOccurrence(nextDate); 459 } 460 461 return occurences; 462 } 463 464 @Override 465 public Date getFirstOccurrence(Date date) 466 { 467 Date eventStartDate = getStartDate(); 468 Date eventEndDate = getEndDate(); 469 470 if (eventEndDate.after(date)) 471 { 472 return eventStartDate; 473 } 474 else 475 { 476 long eventDuringTime = eventEndDate.getTime() - eventStartDate.getTime(); 477 478 Date endDate = eventEndDate; 479 Date startDate = eventStartDate; 480 while (startDate != null && endDate.before(date)) 481 { 482 startDate = RecurrentEventHelper.getNextDate(this, startDate); 483 if (startDate != null) 484 { 485 endDate = new Date(startDate.getTime() + eventDuringTime); 486 } 487 } 488 489 return startDate; 490 } 491 } 492 493 @Override 494 public Date getNextOccurrence(Date date) 495 { 496 return RecurrentEventHelper.getNextDate(this, date); 497 } 498 499 @Override 500 public long getWorkflowId() throws AmetysRepositoryException 501 { 502 try 503 { 504 return getNode().getProperty(PROPERTY_WORKFLOW_ID).getLong(); 505 } 506 catch (RepositoryException e) 507 { 508 throw new AmetysRepositoryException("Unable to get workflowId property", e); 509 } 510 } 511 512 @Override 513 public void setWorkflowId(long workflowId) throws AmetysRepositoryException 514 { 515 Node node = getNode(); 516 517 try 518 { 519 node.setProperty(PROPERTY_WORKFLOW_ID, workflowId); 520 } 521 catch (RepositoryException e) 522 { 523 throw new AmetysRepositoryException("Unable to set workflowId property", e); 524 } 525 } 526 527 @Override 528 public long getCurrentStepId() 529 { 530 throw new UnsupportedOperationException(); 531 } 532 533 @Override 534 public void setCurrentStepId(long stepId) 535 { 536 throw new UnsupportedOperationException(); 537 } 538}