001/* 002 * Copyright 2012 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.calendar.events; 017 018import java.time.LocalDate; 019import java.time.ZoneId; 020import java.time.ZonedDateTime; 021import java.time.format.DateTimeFormatter; 022import java.util.Date; 023 024import org.apache.avalon.framework.logger.LogEnabled; 025import org.apache.avalon.framework.logger.Logger; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.commons.lang.StringUtils; 030 031import org.ametys.cms.contenttype.ContentType; 032import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 033import org.ametys.cms.contenttype.MetadataDefinition; 034import org.ametys.cms.contenttype.MetadataType; 035import org.ametys.cms.repository.Content; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.runtime.parameter.ParameterHelper; 038import org.ametys.runtime.parameter.ParameterHelper.ParameterType; 039 040/** 041 * Helper class that provides a method to check if a date is between two others. 042 */ 043public final class EventHelper implements LogEnabled, Serviceable 044{ 045 /** The Ametys object resolver */ 046 protected static AmetysObjectResolver _resolver; 047 /** The extension point for content types */ 048 protected static ContentTypeExtensionPoint _contentTypeEP; 049 050 private static Logger _logger; 051 052 @Override 053 public void enableLogging(Logger logger) 054 { 055 _logger = logger; 056 } 057 058 @Override 059 public void service(ServiceManager manager) throws ServiceException 060 { 061 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 062 _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 063 } 064 065 /** 066 * Tests if the given date is either: 067 * - equal to the first, if there is no end date. 068 * - between the two dates, if there is a start and end date. 069 * @param dateStr the date to test. 070 * @param startStr start of the interval. 071 * @param endStr end of the interval. 072 * @return true if the date is either equal to the start date or between the start and end date. 073 */ 074 public static boolean isBetween(String dateStr, String startStr, String endStr) 075 { 076 boolean between = false; 077 LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE); 078 LocalDate start = LocalDate.parse(startStr, DateTimeFormatter.ISO_LOCAL_DATE); 079 080 if (StringUtils.isBlank(endStr)) 081 { 082 between = date.equals(start); 083 } 084 else 085 { 086 LocalDate end = LocalDate.parse(endStr, DateTimeFormatter.ISO_LOCAL_DATE); 087 between = (date.isAfter(start) || date.equals(start)) && (date.isBefore(end) || date.equals(end)); 088 } 089 090 return between; 091 } 092 093 /** 094 * Tests if the given event occurs in the given month. 095 * @param monthStartStr The month first day, cannot be null or blank. 096 * @param eventStartStr The event start date, can be null or blank only if the end date is set. 097 * @param eventEndStr The event end date, can be null or blank only if the end date is set. 098 * @return true if the event occurs in the given month. 099 */ 100 public static boolean inMonth(String monthStartStr, String eventStartStr, String eventEndStr) 101 { 102 boolean overlaps = false; 103 104 Date myDate = (Date) ParameterHelper.castValue(monthStartStr, ParameterType.DATE); 105 ZonedDateTime monthStart = myDate.toInstant().atZone(ZoneId.systemDefault()); 106 ZonedDateTime monthEnd = monthStart.toLocalDate().atStartOfDay(monthStart.getZone()).plusMonths(1); 107 108 if (StringUtils.isNotBlank(eventStartStr) && StringUtils.isNotBlank(eventEndStr)) 109 { 110 ZonedDateTime eventStart = ((Date) ParameterHelper.castValue(eventStartStr, ParameterType.DATE)).toInstant().atZone(ZoneId.systemDefault()); 111 ZonedDateTime eventEnd = ((Date) ParameterHelper.castValue(eventEndStr, ParameterType.DATE)).toInstant().atZone(ZoneId.systemDefault()); 112 113 // check that start is before end to avoid an unwanted exception 114 try 115 { 116 // If eventStart equals to eventEnd and equals to monthStart, month#overlaps(Interval) will return false. In our case, we want to consider it actually does. 117 overlaps = eventStartStr.equals(eventEndStr) ? _contains(monthStart, monthEnd, eventStart) 118 : _overlaps(monthStart, monthEnd, eventStart, eventEnd); 119 } 120 catch (IllegalArgumentException e) 121 { 122 // The end is before the start 123 overlaps = false; 124 _logger.error(String.format("Invalid dates of event: the end date (%s) must be greater or equal to the start date (%s). The event will be ignored in calendar view.", eventEndStr, eventStartStr), e); 125 } 126 } 127 else if (StringUtils.isNotBlank(eventStartStr)) 128 { 129 ZonedDateTime eventStart = ((Date) ParameterHelper.castValue(eventStartStr, ParameterType.DATE)).toInstant().atZone(ZoneId.systemDefault()); 130 overlaps = _contains(monthStart, monthEnd, eventStart); 131 } 132 else if (StringUtils.isNotBlank(eventEndStr)) 133 { 134 ZonedDateTime eventEnd = ((Date) ParameterHelper.castValue(eventEndStr, ParameterType.DATE)).toInstant().atZone(ZoneId.systemDefault()); 135 overlaps = _contains(monthStart, monthEnd, eventEnd); 136 } 137 138 return overlaps; 139 } 140 141 /** 142 * Test if an event is between 2 dates 143 * @param start start date 144 * @param end end date 145 * @param event event to check 146 * @return true if the event is between both dates 147 */ 148 private static boolean _contains(ZonedDateTime start, ZonedDateTime end, ZonedDateTime event) 149 { 150 ZonedDateTime realStart; 151 ZonedDateTime realEnd; 152 if (start.isBefore(end)) 153 { 154 realStart = start; 155 realEnd = end; 156 } 157 else 158 { 159 realStart = end; 160 realEnd = start; 161 } 162 return (realStart.isBefore(event) || realStart.isEqual(event)) && realEnd.isAfter(event); 163 } 164 165 /** 166 * Test if 2 period of time overlap 167 * @param start1 start of 1st period 168 * @param end1 end of 1st period 169 * @param start2 start of 2nd period 170 * @param end2 end of 2nd period 171 * @return true if both period overlaps 172 */ 173 private static boolean _overlaps(ZonedDateTime start1, ZonedDateTime end1, ZonedDateTime start2, ZonedDateTime end2) 174 { 175 ZonedDateTime realStart1; 176 ZonedDateTime realEnd1; 177 if (start1.isBefore(end1)) 178 { 179 realStart1 = start1; 180 realEnd1 = end1; 181 } 182 else 183 { 184 realStart1 = end1; 185 realEnd1 = start1; 186 } 187 ZonedDateTime realStart2; 188 ZonedDateTime realEnd2; 189 if (start2.isBefore(end2)) 190 { 191 realStart2 = start2; 192 realEnd2 = end2; 193 } 194 else 195 { 196 realStart2 = end2; 197 realEnd2 = start2; 198 } 199 return (realStart1.isBefore(realEnd2) || realStart1.isEqual(realEnd2)) && realEnd1.isAfter(realStart2); 200 } 201 202 /** 203 * Tests if the given date is either: 204 * @param dateStr the date to test. 205 * @return true if the date is either equal to the start date or between the start and end date. 206 */ 207 public static String nextDay(String dateStr) 208 { 209 ZonedDateTime date = ZonedDateTime.parse(dateStr, DateTimeFormatter.ISO_DATE_TIME); 210 211 return ParameterHelper.getISODateTimeFormatter().format(date.plusDays(1)); 212 } 213 214 /** 215 * Tests if the given date is either: 216 * @param dateStr the date to test. 217 * @param format the format ("date" or "basicDate"). 218 * @return true if the date is either equal to the start date or between the start and end date. 219 */ 220 public static String nextDay(String dateStr, String format) 221 { 222 String nextDayStr = ""; 223 224 ZonedDateTime date = ZonedDateTime.parse(dateStr, ParameterHelper.getISODateTimeFormatter()); 225 226 ZonedDateTime nextDay = date.plusDays(1); 227 228 if ("basicDate".equals(format)) 229 { 230 nextDayStr = DateTimeFormatter.BASIC_ISO_DATE.format(nextDay); 231 } 232 else 233 { 234 nextDayStr = DateTimeFormatter.ISO_LOCAL_DATE.format(nextDay); 235 } 236 237 return nextDayStr; 238 } 239 240 /** 241 * Tests if the given metadata for the given content is of type {@link MetadataType#DATETIME} 242 * @param contentId The id of the content 243 * @param metadataName The metadata name 244 * @return true if the given metadata for the given content is of type {@link MetadataType#DATETIME} 245 */ 246 public static boolean isDatetime(String contentId, String metadataName) 247 { 248 Content content = _resolver.resolveById(contentId); 249 String[] cTypes = content.getTypes(); 250 251 for (String cTypeId : cTypes) 252 { 253 ContentType cType = _contentTypeEP.getExtension(cTypeId); 254 255 if (cType.hasMetadataDefinition(metadataName)) 256 { 257 MetadataDefinition metadataDef = cType.getMetadataDefinition(metadataName); 258 MetadataType type = metadataDef.getType(); 259 260 return MetadataType.DATETIME.equals(type); 261 } 262 } 263 264 return false; 265 } 266}