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 org.apache.avalon.framework.logger.LogEnabled;
019import org.apache.avalon.framework.logger.Logger;
020import org.apache.avalon.framework.service.ServiceException;
021import org.apache.avalon.framework.service.ServiceManager;
022import org.apache.avalon.framework.service.Serviceable;
023import org.apache.commons.lang.StringUtils;
024import org.joda.time.DateTime;
025import org.joda.time.Interval;
026import org.joda.time.format.ISODateTimeFormat;
027
028import org.ametys.cms.contenttype.ContentType;
029import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
030import org.ametys.cms.contenttype.MetadataDefinition;
031import org.ametys.cms.contenttype.MetadataType;
032import org.ametys.cms.repository.Content;
033import org.ametys.plugins.repository.AmetysObjectResolver;
034import org.ametys.runtime.parameter.ParameterHelper;
035import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
036
037/**
038 * Helper class that provides a method to check if a date is between two others.
039 */
040public final class EventHelper implements LogEnabled, Serviceable
041{    
042    /** The Ametys object resolver */
043    protected static AmetysObjectResolver _resolver;
044    /** The extension point for content types */
045    protected static ContentTypeExtensionPoint _contentTypeEP;
046    
047    private static Logger _logger;
048
049    @Override
050    public void enableLogging(Logger logger)
051    {
052        _logger = logger;
053    }
054    
055    @Override
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
059        _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
060    }
061    
062    /**
063     * Tests if the given date is either:
064     * - equal to the first, if there is no end date.
065     * - between the two dates, if there is a start and end date.
066     * @param dateStr the date to test.
067     * @param startStr start of the interval.
068     * @param endStr end of the interval.
069     * @return true if the date is either equal to the start date or between the start and end date.
070     */
071    public static boolean isBetween(String dateStr, String startStr, String endStr)
072    {
073        boolean between = false;
074        
075        DateTime date = ISODateTimeFormat.date().parseDateTime(dateStr);
076        DateTime start = ISODateTimeFormat.date().parseDateTime(startStr);
077        
078        if (StringUtils.isBlank(endStr))
079        {
080            between = date.equals(start);
081        }
082        else
083        {
084            DateTime end = ISODateTimeFormat.date().parseDateTime(endStr);
085            between = (date.isAfter(start) || date.equals(start)) && (date.isBefore(end) || date.equals(end));
086        }
087        
088        return between;
089    }
090    
091    /**
092     * Tests if the given event occurs in the given month.
093     * @param monthStartStr The month first day, cannot be null or blank.
094     * @param eventStartStr The event start date, can be null or blank only if the end date is set.
095     * @param eventEndStr The event end date, can be null or blank only if the end date is set.
096     * @return true if the event occurs in the given month.
097     */
098    public static boolean inMonth(String monthStartStr, String eventStartStr, String eventEndStr)
099    {
100        boolean overlaps = false;
101        
102        DateTime monthStart = new DateTime(ParameterHelper.castValue(monthStartStr, ParameterType.DATE));
103        DateTime monthEnd = monthStart.withDayOfMonth(1).withMillisOfDay(0).plusMonths(1);
104        Interval month = new Interval(monthStart, monthEnd);
105        
106        if (StringUtils.isNotBlank(eventStartStr) && StringUtils.isNotBlank(eventEndStr))
107        {
108            DateTime eventStart = new DateTime(ParameterHelper.castValue(eventStartStr, ParameterType.DATE));
109            DateTime eventEnd = new DateTime(ParameterHelper.castValue(eventEndStr, ParameterType.DATE));
110            
111            // check that start is before end to avoid an unwanted exception
112            try
113            {
114                // 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.
115                overlaps = eventStartStr.equals(eventEndStr) ? month.contains(eventStart) 
116                                                             : month.overlaps(new Interval(eventStart, eventEnd));
117            }
118            catch (IllegalArgumentException e)
119            {
120                // The end is before the start
121                overlaps = false;
122                _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);
123            }
124        }
125        else if (StringUtils.isNotBlank(eventStartStr))
126        {
127            DateTime date = new DateTime(ParameterHelper.castValue(eventStartStr, ParameterType.DATE));
128            overlaps = month.contains(date);
129        }
130        else if (StringUtils.isNotBlank(eventEndStr))
131        {
132            DateTime date = new DateTime(ParameterHelper.castValue(eventEndStr, ParameterType.DATE));
133            overlaps = month.contains(date);
134        }
135        
136        return overlaps;
137    }
138    
139    /**
140     * Tests if the given date is either:
141     * @param dateStr the date to test.
142     * @return true if the date is either equal to the start date or between the start and end date.
143     */
144    public static String nextDay(String dateStr)
145    {
146        DateTime date = ISODateTimeFormat.dateTime().parseDateTime(dateStr);
147        
148        return ISODateTimeFormat.dateTime().print(date.plusDays(1));
149    }
150    
151    /**
152     * Tests if the given date is either:
153     * @param dateStr the date to test.
154     * @param format the format ("date" or "basicDate").
155     * @return true if the date is either equal to the start date or between the start and end date.
156     */
157    public static String nextDay(String dateStr, String format)
158    {
159        String nextDayStr = "";
160        
161        DateTime date = ISODateTimeFormat.dateTime().parseDateTime(dateStr);
162        
163        DateTime nextDay = date.plusDays(1);
164        
165        if ("basicDate".equals(format))
166        {
167            nextDayStr = ISODateTimeFormat.basicDate().print(nextDay);
168        }
169        else
170        {
171            nextDayStr = ISODateTimeFormat.date().print(nextDay);
172        }
173        
174        return nextDayStr;
175    }
176    
177    /**
178     * Tests if the given metadata for the given content is of type {@link MetadataType#DATETIME}
179     * @param contentId The id of the content
180     * @param metadataName The metadata name
181     * @return true if the given metadata for the given content is of type {@link MetadataType#DATETIME}
182     */
183    public static boolean isDatetime(String contentId, String metadataName)
184    {
185        Content content = _resolver.resolveById(contentId);
186        String[] cTypes = content.getTypes();
187        
188        for (String cTypeId : cTypes)
189        {
190            ContentType cType = _contentTypeEP.getExtension(cTypeId);
191            
192            if (cType.hasMetadataDefinition(metadataName))
193            {
194                MetadataDefinition metadataDef = cType.getMetadataDefinition(metadataName);
195                MetadataType type = metadataDef.getType();
196                
197                return MetadataType.DATETIME.equals(type);
198            }
199        }
200        
201        return false;
202    }
203}