Back to index

plone3  3.1.7
CalendarTool.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ CMFCalendar portal_calendar tool.
00014 
00015 $Id: CalendarTool.py 77004 2007-06-24 08:57:54Z yuppie $
00016 """
00017 
00018 import calendar
00019 
00020 from AccessControl import ClassSecurityInfo
00021 from DateTime import DateTime
00022 from Globals import InitializeClass
00023 from OFS.SimpleItem import SimpleItem
00024 from zope.interface import implements
00025 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00026 
00027 from Products.CMFCore.utils import getToolByName
00028 from Products.CMFCore.utils import UniqueObject
00029 
00030 from interfaces import ICalendarTool
00031 from permissions import ManagePortal
00032 
00033 class CalendarTool (UniqueObject, SimpleItem):
00034 
00035     """ A tool for encapsulating how calendars work and are displayed """
00036 
00037     id = 'portal_calendar'
00038     meta_type= 'CMF Calendar Tool'
00039     security = ClassSecurityInfo()
00040 
00041     implements(ICalendarTool)
00042 
00043     calendar_types = ('Event',)
00044     calendar_states = ('published',)
00045     use_session = False
00046     firstweekday = 6 # 6 is Sunday
00047 
00048     manage_options = (({'label' : 'Overview', 'action' : 'manage_overview'},
00049                        {'label' : 'Configure', 'action' : 'manage_configure'},
00050                       ) + SimpleItem.manage_options)
00051 
00052     #
00053     #   ZMI methods
00054     #
00055     security.declareProtected( ManagePortal, 'manage_overview' )
00056     manage_overview = PageTemplateFile('www/explainCalendarTool', globals(),
00057                                    __name__='manage_overview')
00058 
00059     security.declareProtected( ManagePortal, 'manage_configure' )
00060     manage_configure = PageTemplateFile('www/configureCalendarTool', globals(),
00061                                    __name__='manage_configure')
00062 
00063     security.declareProtected( ManagePortal, 'edit_configuration' )
00064     def edit_configuration( self
00065                           , show_types
00066                           , use_session
00067                           , show_states=None
00068                           , firstweekday=None
00069                           ):
00070         """ Change the configuration of the calendar tool 
00071         """
00072         # XXX: this method violates the rules for tools/utilities:
00073         # it depends on self.REQUEST
00074         self.calendar_types = tuple(show_types)
00075         self.use_session = bool(use_session)
00076 
00077         if show_states is not None:
00078             self.calendar_states = tuple(show_states)
00079 
00080         if firstweekday is not None:
00081             try:
00082                 fwd = int(firstweekday)
00083 
00084                 if 0 <= fwd <= 6:
00085                     # Do nothing with illegal values
00086                     self.firstweekday = fwd
00087             except ValueError:
00088                 # Do nothing with illegal values
00089                 pass
00090 
00091         if hasattr(self.REQUEST, 'RESPONSE'):
00092             self.REQUEST.RESPONSE.redirect('manage_configure')
00093 
00094     security.declarePrivate('_getCalendar')
00095     def _getCalendar(self):
00096         """ Wrapper to ensure we set the first day of the week every time
00097         """
00098         calendar.setfirstweekday(self.getFirstWeekDay())
00099         return calendar
00100 
00101     security.declarePublic('getFirstWeekDay')
00102     def getFirstWeekDay(self):
00103         """ Get our first weekday setting
00104         """
00105         return self.firstweekday
00106 
00107     security.declarePublic('getCalendarTypes')
00108     def getCalendarTypes(self):
00109         """ Returns a list of type that will show in the calendar 
00110         """
00111         return self.calendar_types
00112 
00113     security.declarePublic('getCalendarStates')
00114     def getCalendarStates(self):
00115         """ Returns a list of workflow states that will show in the calendar 
00116         """
00117         return self.calendar_states
00118 
00119     security.declarePublic('getUseSession')
00120     def getUseSession(self):
00121         """ Returns the Use_Session option 
00122         """
00123         return bool(self.use_session)
00124 
00125     security.declarePublic('getDays')
00126     def getDays(self):
00127         """ Returns a list of days with the correct start day first 
00128         """
00129         return self._getCalendar().weekheader(2).split()
00130 
00131     security.declarePublic('getWeeksList')
00132     def getWeeksList(self, month='1', year='2002'):
00133         """ Return a series of weeks, each containing an integer day number.
00134         A day number of 0 means that day is in the previous or next month.
00135         """
00136         year = int(year)
00137         month = int(month)
00138         # daysByWeek is a list of days inside a list of weeks, like so:
00139         # [[0, 1, 2, 3, 4, 5, 6],
00140         #  [7, 8, 9, 10, 11, 12, 13],
00141         #  [14, 15, 16, 17, 18, 19, 20],
00142         #  [21, 22, 23, 24, 25, 26, 27],
00143         #  [28, 29, 30, 31, 0, 0, 0]]
00144         daysByWeek = self._getCalendar().monthcalendar(year, month)
00145 
00146         return daysByWeek
00147 
00148     security.declarePublic('getEventsForCalendar')
00149     def getEventsForCalendar(self, month='1', year='2002'):
00150         """ recreates a sequence of weeks, by days each day is a mapping.
00151             {'day': #, 'url': None}
00152         """
00153         year = int(year)
00154         month = int(month)
00155         # daysByWeek is a list of days inside a list of weeks, like so:
00156         # [[0, 1, 2, 3, 4, 5, 6],
00157         #  [7, 8, 9, 10, 11, 12, 13],
00158         #  [14, 15, 16, 17, 18, 19, 20],
00159         #  [21, 22, 23, 24, 25, 26, 27],
00160         #  [28, 29, 30, 31, 0, 0, 0]]
00161         daysByWeek = self._getCalendar().monthcalendar(year, month)
00162         weeks = []
00163 
00164         events = self.catalog_getevents(year, month)
00165 
00166         for week in daysByWeek:
00167             days = []
00168             for day in week:
00169                 if events.has_key(day):
00170                     days.append(events[day])
00171                 else:
00172                     days.append({'day': day, 'event': 0, 'eventslist':[]})
00173 
00174             weeks.append(days)
00175 
00176         return weeks
00177 
00178     security.declarePublic('catalog_getevents')
00179     def catalog_getevents(self, year, month):
00180         """ given a year and month return a list of days that have events 
00181         """
00182         # XXX: this method violates the rules for tools/utilities:
00183         # it depends on a non-utility tool
00184         year = int(year)
00185         month = int(month)
00186         last_day = self._getCalendar().monthrange(year, month)[1]
00187         first_date = self.getBeginAndEndTimes(1, month, year)[0]
00188         last_date = self.getBeginAndEndTimes(last_day, month, year)[1]
00189 
00190         ctool = getToolByName(self, 'portal_catalog')
00191         query = ctool(
00192                         portal_type=self.getCalendarTypes(),
00193                         review_state=self.getCalendarStates(),
00194                         start={'query': last_date, 'range': 'max'},
00195                         end={'query': first_date, 'range': 'min'},
00196                         sort_on='start' )
00197 
00198         # compile a list of the days that have events
00199         eventDays={}
00200         for daynumber in range(1, 32): # 1 to 31
00201             eventDays[daynumber] = {'eventslist': [],
00202                                     'event': 0,
00203                                     'day': daynumber}
00204         includedevents = []
00205         for result in query:
00206             if result.getRID() in includedevents:
00207                 break
00208             else:
00209                 includedevents.append(result.getRID())
00210             event={}
00211             # we need to deal with events that end next month
00212             if  result.end.month() != month:
00213                 # doesn't work for events that last ~12 months
00214                 # fix it if it's a problem, otherwise ignore
00215                 eventEndDay = last_day
00216                 event['end'] = None
00217             else:
00218                 eventEndDay = result.end.day()
00219                 event['end'] = result.end.Time()
00220             # and events that started last month
00221             if result.start.month() != month:  # same as above (12 month thing)
00222                 eventStartDay = 1
00223                 event['start'] = None
00224             else:
00225                 eventStartDay = result.start.day()
00226                 event['start'] = result.start.Time()
00227 
00228             event['title'] = result.Title or result.getId
00229 
00230             if eventStartDay != eventEndDay:
00231                 allEventDays = range(eventStartDay, eventEndDay+1)
00232                 eventDays[eventStartDay]['eventslist'].append(
00233                         {'end': None,
00234                          'start': result.start.Time(),
00235                          'title': event['title']} )
00236                 eventDays[eventStartDay]['event'] = 1
00237 
00238                 for eventday in allEventDays[1:-1]:
00239                     eventDays[eventday]['eventslist'].append(
00240                         {'end': None,
00241                          'start': None,
00242                          'title': event['title']} )
00243                     eventDays[eventday]['event'] = 1
00244 
00245                 if result.end == result.end.earliestTime():
00246                     last_day_data = eventDays[allEventDays[-2]]
00247                     last_days_event = last_day_data['eventslist'][-1]
00248                     last_days_event['end'] = (result.end-1).latestTime().Time()
00249                 else:
00250                     eventDays[eventEndDay]['eventslist'].append( 
00251                         { 'end': result.end.Time()
00252                         , 'start': None, 'title': event['title']} )
00253                     eventDays[eventEndDay]['event'] = 1
00254             else:
00255                 eventDays[eventStartDay]['eventslist'].append(event)
00256                 eventDays[eventStartDay]['event'] = 1
00257             # This list is not uniqued and isn't sorted
00258             # uniquing and sorting only wastes time
00259             # and in this example we don't need to because
00260             # later we are going to do an 'if 2 in eventDays'
00261             # so the order is not important.
00262             # example:  [23, 28, 29, 30, 31, 23]
00263         return eventDays
00264 
00265     security.declarePublic('getEventsForThisDay')
00266     def getEventsForThisDay(self, thisDay):
00267         """ given an exact day return ALL events that:
00268             A) Start on this day  OR
00269             B) End on this day  OR
00270             C) Start before this day  AND  end after this day
00271         """
00272         # XXX: this method violates the rules for tools/utilities:
00273         # it depends on a non-utility tool
00274         day, month, year = ( int(thisDay.day())
00275                            , int(thisDay.month())
00276                            , int(thisDay.year())
00277                            )
00278 
00279         first_date, last_date = self.getBeginAndEndTimes(day, month, year)
00280         zone = first_date.localZone()
00281         after_midnight_str = '%d-%02d-%02d 00:01:00 %s' % (year,month,day,zone)
00282         after_midnight = DateTime(after_midnight_str)
00283 
00284         # Get all events that Start on this day
00285         ctool = getToolByName(self, 'portal_catalog')
00286         query = ctool(
00287                         portal_type=self.getCalendarTypes(),
00288                         review_state=self.getCalendarStates(),
00289                         start={'query': (first_date, last_date),
00290                                'range': 'minmax'} )
00291 
00292         # Get all events that End on this day
00293         query += ctool(
00294                          portal_type=self.getCalendarTypes(),
00295                          review_state=self.getCalendarStates(),
00296                          end={'query': (after_midnight, last_date),
00297                               'range': 'minmax'} )
00298 
00299         # Get all events that Start before this day AND End after this day
00300         query += ctool(
00301                          portal_type=self.getCalendarTypes(),
00302                          review_state=self.getCalendarStates(),
00303                          start={'query': first_date, 'range': 'max'},
00304                          end={'query': last_date, 'range': 'min'} )
00305 
00306         # Unique the results
00307         results = []
00308         rids = []
00309         for item in query:
00310             rid = item.getRID()
00311             if not rid in rids:
00312                 results.append(item)
00313                 rids.append(rid)
00314 
00315         def sort_function(x,y):
00316             z = cmp(x.start,y.start)
00317             if not z:
00318                 return cmp(x.end,y.end)
00319             return z
00320 
00321         # Sort by start date
00322         results.sort(sort_function)
00323 
00324         return results
00325 
00326     security.declarePublic('getPreviousMonth')
00327     def getPreviousMonth(self, month, year):
00328         """ Get a DateTime object for one month prior to the given year/month
00329         """
00330         month = int(month)
00331         year = int(year)
00332 
00333         if month == 0 or month == 1:
00334             month, year = 12, year - 1
00335         else:
00336             month -= 1
00337 
00338         return DateTime(year, month, 1)
00339 
00340     security.declarePublic('getNextMonth')
00341     def getNextMonth(self, month, year):
00342         """ Get a DateTime object for one month after the given year/month
00343         """
00344         month = int(month)
00345         year = int(year)
00346 
00347         if month == 12:
00348             month, year = 1, year + 1
00349         else:
00350             month += 1
00351 
00352         return DateTime(year, month, 1)
00353 
00354     security.declarePublic('getBeginAndEndTimes')
00355     def getBeginAndEndTimes(self, day, month, year):
00356         """ Get two DateTime objects representing the beginning and end
00357         of the given day
00358         """
00359         day = int(day)
00360         month = int(month)
00361         year = int(year)
00362 
00363         begin = DateTime('%d/%02d/%02d 00:00:00' % (year, month, day))
00364         end = DateTime('%d/%02d/%02d 23:59:59' % (year, month, day))
00365 
00366         return (begin, end)
00367 
00368 InitializeClass(CalendarTool)