[CMF-checkins] CVS: CMF/CMFCalendar - CREDITS.txt:1.1 CalendarTool.py:1.1 tool.gif:1.1 README.txt:1.2.34.1 TODO.txt:1.2.34.1 __init__.py:1.3.4.1

Chris Withers chrisw@nipltd.com
Thu, 2 May 2002 09:52:20 -0400


Update of /cvs-repository/CMF/CMFCalendar
In directory cvs.zope.org:/tmp/cvs-serv22207

Modified Files:
      Tag: cmfcalendar_dev
	README.txt TODO.txt __init__.py 
Added Files:
	CREDITS.txt CalendarTool.py tool.gif 
Log Message:
Initial checkin of AndyD's CMFCalendar.

=== Added File CMF/CMFCalendar/CREDITS.txt ===
-- CREDITS.TXT --

Orignal Event meta_type and CMFCalendar by Zope Corporation.

The implementation of the calendar and the calendar_tool was
completed by Andy Dawkins (New Information Paradigms Ltd)

Andy originally implemented the Calendar design in plone
(www.plone.org) with help from the plone development team
(Alexander Limi, Vidar Andersen and Alan Runyan)


=== Added File CMF/CMFCalendar/CalendarTool.py ===
########################################################################
#
# Calendar Tool by Andy Dawkins (New Information Paradigms Ltd)
#
# Converted to a CMF Tool by Alan Runyan
#
# Additional Modification for the CMFCalendar by Andy Dawkins 29/04/2002
#
########################################################################


import calendar
calendar.setfirstweekday(6) #start day  Mon(0)-Sun(6)
from DateTime import DateTime

from Products.CMFCore.utils import UniqueObject
from Products.CMFCore.utils import _checkPermission, _getAuthenticatedUser, limitGrantedRoles
from Products.CMFCore.utils import getToolByName, _dtmldir
from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFCore import CMFCorePermissions
from Products.PageTemplates.PageTemplateFile import PageTemplateFile

class CalendarTool (UniqueObject, SimpleItem):
    """ a calendar tool for encapsualting how calendars work and are displayed """
    id = 'portal_calendar'
    meta_type= 'CMF Calendar Tool'
    security = ClassSecurityInfo()
    plone_tool = 1

    manage_options = ( ({ 'label' : 'Overview', 'action' : 'manage_overview' }
                     ,  { 'label' : 'Configure', 'action' : 'manage_configure' }
                     ,
                     ) + SimpleItem.manage_options
                     )

    #
    #   ZMI methods
    #
    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'manage_overview' )    
    manage_overview = PageTemplateFile('www/explainCalendarTool', globals(),
                                   __name__='manage_overview')

    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'manage_configure' )    
    manage_configure = PageTemplateFile('www/configureCalendarTool', globals(),
                                   __name__='manage_configure')
    
    def __init__(self):
        self.calendar_types = ['Event']
        self.use_session = "True"

    security.declareProtected( CMFCorePermissions.ManagePortal
                             , 'edit_configuration' )
    def edit_configuration(self, show_types, use_session):
        """ Change the configuration of the calendar tool """
        self.calendar_types = show_types
        self.use_session = use_session
        if hasattr(self.REQUEST, 'RESPONSE'):
            self.REQUEST.RESPONSE.redirect('manage_configure')
        
    security.declarePublic('getCalendarTypes')
    def getCalendarTypes(self):
        """ Returns a list of type that will show in the calendar """
        return self.calendar_types

    security.declarePublic('getUseSession')
    def getUseSession(self):
        """ Returns the Use_Session option """
        return self.use_session

    security.declarePublic('getDays')
    def getDays(self):
        """ Returns a list of days with the correct start day first """        
        import string
        return string.split(calendar.weekheader(2),' ')
        
    security.declarePublic('getWeeksList')
    def getWeeksList(self, month='1', year='2002'):
        """Creates a series of weeks, each of which contains an integer day number.
           A day number of 0 means that day is in the previous or next month.
        """
        # daysByWeek is a list of days inside a list of weeks, like so:
        # [[0, 1, 2, 3, 4, 5, 6],
        #  [7, 8, 9, 10, 11, 12, 13],
        #  [14, 15, 16, 17, 18, 19, 20],
        #  [21, 22, 23, 24, 25, 26, 27],
        #  [28, 29, 30, 31, 0, 0, 0]]
        daysByWeek=calendar.monthcalendar(year, month)
    
        return daysByWeek

    security.declarePublic('getEventsForCalendar')
    def getEventsForCalendar(self, month='1', year='2002'):
        """ recreates a sequence of weeks, by days each day is a mapping.
            {'day': #, 'url': None}
        """
        year=int(year)
        month=int(month)
        # daysByWeek is a list of days inside a list of weeks, like so:
        # [[0, 1, 2, 3, 4, 5, 6],
        #  [7, 8, 9, 10, 11, 12, 13],
        #  [14, 15, 16, 17, 18, 19, 20],
        #  [21, 22, 23, 24, 25, 26, 27],
        #  [28, 29, 30, 31, 0, 0, 0]]
        daysByWeek=calendar.monthcalendar(year, month)
        weeks=[]
        
        events=self.catalog_getevents(year, month)
    
        for week in daysByWeek:
            days=[]
            for day in week:
                if events.has_key(day):
                    days.append(events[day])
                else:
                    days.append({'day': day, 'event': 0, 'eventslist':[]})
                
            weeks.append(days)
            
        return weeks
    
    security.declarePublic('catalog_getevents')
    def catalog_getevents(self, year, month):
        """ given a year and month return a list of days that have events """
        first_date=DateTime(str(month)+'/1/'+str(year))
        last_day=calendar.monthrange(year, month)[1]
        last_date=DateTime(str(month)+'/'+str(last_day)+'/'+str(year))
    
        query=self.portal_catalog(Type=self.calendar_types,
                                  review_state='published',	                          
                                  start=(first_date, last_date),
                                  start_usage='range:min:max',
                                  sort_on='start')
        # I don't like doing two searches
        # What i want to do is
        # start date => 1/1/2002 and start date <= 31/1/2002
        # or
        # end date => 1/1/2002 and end date <= 31/1/2002
        # but I don't know how to do that in one search query :(  - AD

        # if you look at calendar_slot you can see how to do this in 1 query - runyaga
        query+=self.portal_catalog(Type=self.calendar_types,
                                   review_state='published',
                                   end=(first_date, last_date),
                                   end_usage='range:min:max',
                                   sort_on='end')
        
        # compile a list of the days that have events
        eventDays={}
        for daynumber in range(1, 32): # 1 to 31
            eventDays[daynumber] = {'eventslist':[], 'event':0, 'day':daynumber}
        includedevents = []
        for result in query:
            if result.getRID() in includedevents:
                break
            else:
                includedevents.append(result.getRID())
            event={}
            # we need to deal with events that end next month
            if  result.end.month() != month:  # doesn't work for events that last ~12 months - fix it if it's a problem, otherwise ignore
                eventEndDay = last_day
                event['end'] = None
            else:
                eventEndDay = result.end.day()
                event['end'] = result.end.Time()
            # and events that started last month
            if result.start.month() != month:  # same as above re: 12 month thing
                eventStartDay = 1
                event['start'] = None
            else:
                eventStartDay = result.start.day()
                event['start'] = result.start.Time()
            event['title'] = result.Title or result.id
            if eventStartDay != eventEndDay:
                allEventDays = range(eventStartDay, eventEndDay+1)
                eventDays[eventStartDay]['eventslist'].append({'end':None, 'start':result.start.Time(), 'title':result.Title})
                eventDays[eventStartDay]['event'] = 1
                for eventday in allEventDays[1:-1]:
                    eventDays[eventday]['eventslist'].append({'end':None, 'start':None, 'title':result.Title})
                    eventDays[eventday]['event'] = 1
                eventDays[eventEndDay]['eventslist'].append({'end':result.end.Time(), 'start':None, 'title':result.Title})
                eventDays[eventEndDay]['event'] = 1
            else:
                eventDays[eventStartDay]['eventslist'].append(event)
                eventDays[eventStartDay]['event'] = 1
            # This list is not uniqued and isn't sorted
            # uniquing and sorting only wastes time
            # and in this example we don't need to because
            # later we are going to do an 'if 2 in eventDays'
            # so the order is not important.
            # example:  [23, 28, 29, 30, 31, 23]
        return eventDays

    security.declarePublic('getEventsForThisDay')
    def getEventsForThisDay(self, thisDay):
        """ given an exact day return ALL events that:
            A) Start on this day  OR
            B) End on this day  OR
            C) Start before this day  AND  end after this day"""
        
        catalog = self.portal_catalog
        
        first_date, last_date = self.getBeginAndEndTimes(thisDay.day(), thisDay.month(), thisDay.year())
        #first_date=DateTime(thisDay.Date()+" 00:00:00")
        #last_date=DateTime(thisDay.Date()+" 23:59:59")

        # Get all events that Start on this day
        query=self.portal_catalog(Type=self.calendar_types,
                                  review_state='published',	                          
                                  start=(first_date,last_date),
                                  start_usage='range:min:max')
        
        # Get all events that End on this day
        query+=self.portal_catalog(Type=self.calendar_types,
                                  review_state='published',	                          
                                  end=(first_date,last_date),
                                  end_usage='range:min:max')

        # Get all events that Start before this day AND End after this day
        query+=self.portal_catalog(Type=self.calendar_types,
                                  review_state='published',
                                  start=first_date,
                                  start_usage='range:max',
                                  end=last_date,
                                  end_usage='range:min')

        # Unique the results
        results = []
        rids = []
        for item in query:
            rid = item.getRID()
            if not rid in rids:
                results.append(item)
                rids.append(rid)
                
        def sort_function(x,y):
            z = cmp(x.start,y.start)
            if not z: 
                return cmp(x.end,y.end)
            return z
        
        # Sort by start date
        results.sort(sort_function)
                
        return results
                
        
    security.declarePublic('getPreviousMonth')
    def getPreviousMonth(self, month, year):
        # given any particular year and month, this method will return a datetime object
        # for one month prior
        
        try: month=int(month)
        except: raise "Calendar Type Error", month
        try: year=int(year)
        except: raise "Calendar Type Error", year
        
        if month==0 or month==1:
            month, year = 12, year - 1
        else:
            month-=1
        
        return DateTime(str(month) + '/1/' + str(year))    
    
    security.declarePublic('getNextMonth')
    def getNextMonth(self, month, year):
        # given any particular year and month, this method will return a datetime object
        # for one month after
    
        try: month=int(month)
        except: raise "Calendar Type Error", month
        try: year=int(year)
        except: raise "Calendar Type Error", year
        
        if month==12:
            month, year = 1, year + 1
        else:
            month+=1
        
        return DateTime(str(month) + '/1/' + str(year))

    security.declarePublic('getBeginAndEndTimes')
    def getBeginAndEndTimes(self, day, month, year):
        # Given any day, month and year this method returns 2 DateTime objects
        # That represent the exact start and the exact end of that particular day.
        
        day=str(day)
        month=str(month)
        year=str(year)
        
        begin=DateTime(month+'/'+day+'/'+year+' 12:00:00AM')
        end=DateTime(month+'/'+day+'/'+year+' 11:59:59PM')
        
        return (begin, end)    
        
    
InitializeClass(CalendarTool)


=== Added File CMF/CMFCalendar/tool.gif ===
  <Binary-ish file>

=== CMF/CMFCalendar/README.txt 1.2 => 1.2.34.1 ===
   of the object.
 
-  At the time of this writing, this product is incomplete.  The
-  event is complete, and can stand alone, but will be
-  accompanyied by the CMFCalendar in the next release.  We wished
-  to get something out quickly, so we'll be living by the axiom:
-  'Release early; Release often' until all it's components are
-  completed.
+  The event portal_type is complete, and can be stand alone, but
+  now accompanyied by the CMFCalendar which is fully functional
+  and tested.
+
+  After installing the CMFCalendar you should notice a calendar
+  appear in your CMF.  This is fully customisable to your portals
+  needs.
 
   See the INSTALL.txt file for how to get the product installed
   within your CMF.


=== CMF/CMFCalendar/TODO.txt 1.2 => 1.2.34.1 ===
 
  o Add the calendar product itself, at rev 0.1 there is only an
-   event object.
+   event object. -- Done (Andy Dawkins - 1st May 2002)
 
  o other todo items as I think of them....


=== CMF/CMFCalendar/__init__.py 1.3 => 1.3.4.1 ===
 from Products.CMFCore.DirectoryView import registerDirectory
 import EventPermissions
+import CalendarTool
 
 import sys
 this_module = sys.modules[ __name__ ]
@@ -24,6 +25,7 @@
 contentConstructors = (Event.addEvent,)
 contentClasses = (Event.Event,)
 
+tools = ( CalendarTool.CalendarTool, )
 
 z_bases = utils.initializeBasesPhase1( contentClasses, this_module )
 
@@ -34,9 +36,12 @@
 # Make the skins available as DirectoryViews
 registerDirectory('skins', globals())
 registerDirectory('skins/calendar', globals())
-registerDirectory('skins/zpt_calendar', globals())
 
 def initialize( context ):
+    utils.ToolInit('CMFCalendar Tool', tools=tools,
+                   product_name='CMFCalendar', icon='tool.gif',
+                   ).initialize( context )
+    
     utils.initializeBasesPhase2( z_bases, context )
     context.registerHelpTitle('CMF Calendar Help')
     context.registerHelp(directory='help')