vendor/CMF/1.6.1/CMFCalendar

changeset 0:7f637311ea2d CMFCalendar tip

CMF 1.6.1 vendor import
author fguillaume
date Tue, 13 Jun 2006 14:57:59 +0000
parents
children
files CREDITS.txt CalendarTool.py DEPENDENCIES.txt Event.py EventPermissions.py Extensions/Install.py Extensions/__init__.py INSTALL.txt README.txt TODO.txt __init__.py exceptions.py image_sources/event_info_tab.psd permissions.py profiles/default/catalog.xml profiles/default/import_steps.xml profiles/default/skins.xml profiles/default/toolset.xml profiles/default/types.xml profiles/default/types/Event.xml setuphandlers.py skins/calendar/calendar.gif skins/calendar/event_edit.py skins/calendar/event_edit_form.dtml skins/calendar/event_icon.gif skins/calendar/event_info_tab.gif skins/calendar/event_view.dtml skins/calendar/space.gif skins/zpt_calendar/CalendarStyle.css skins/zpt_calendar/calendarBox.pt skins/zpt_calendar/calendar_day_view.pt skins/zpt_calendar/event_edit_control.py skins/zpt_calendar/event_edit_form.py skins/zpt_calendar/event_edit_template.pt skins/zpt_calendar/event_icon.gif skins/zpt_calendar/event_view.pt skins/zpt_calendar/getDaysClass.py skins/zpt_calendar/getEndAsString.py skins/zpt_calendar/getMonthAndYear.py skins/zpt_calendar/getNextDayLink.py skins/zpt_calendar/getNextMonthLink.py skins/zpt_calendar/getPreviousDayLink.py skins/zpt_calendar/getPreviousMonthLink.py skins/zpt_calendar/getStartAsString.py tests/__init__.py tests/test_Calendar.py tests/test_Event.py tool.gif version.txt www/configureCalendarTool.zpt www/explainCalendarTool.zpt
diffstat 51 files changed, 3291 insertions(+), 0 deletions(-) [+]
line diff
     1.1 new file mode 100644
     1.2 --- /dev/null
     1.3 +++ b/CREDITS.txt
     1.4 @@ -0,0 +1,13 @@
     1.5 +-- CREDITS.TXT --
     1.6 +
     1.7 +Orignal Event meta_type and CMFCalendar by Zope Corporation.
     1.8 +
     1.9 +The implementation of the calendar and the calendar_tool was
    1.10 +completed by Andy Dawkins (New Information Paradigms Ltd)
    1.11 +
    1.12 +Andy originally implemented the Calendar design in plone
    1.13 +(www.plone.org) with help from the plone development team
    1.14 +(Alexander Limi, Vidar Andersen and Alan Runyan)
    1.15 +
    1.16 +CalendarTool was converted to a CMF Tool by Alan Runyan.
    1.17 +Additional Modification for the CMFCalendar by Andy Dawkins 29/04/2002
     2.1 new file mode 100644
     2.2 --- /dev/null
     2.3 +++ b/CalendarTool.py
     2.4 @@ -0,0 +1,318 @@
     2.5 +##############################################################################
     2.6 +#
     2.7 +# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
     2.8 +#
     2.9 +# This software is subject to the provisions of the Zope Public License,
    2.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    2.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    2.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    2.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    2.14 +# FOR A PARTICULAR PURPOSE.
    2.15 +#
    2.16 +##############################################################################
    2.17 +""" CMFCalendar portal_calendar tool.
    2.18 +
    2.19 +$Id: CalendarTool.py 66245 2006-03-28 17:02:36Z jens $
    2.20 +"""
    2.21 +
    2.22 +import calendar
    2.23 +calendar.setfirstweekday(6) #start day  Mon(0)-Sun(6)
    2.24 +
    2.25 +from AccessControl import ClassSecurityInfo
    2.26 +from DateTime import DateTime
    2.27 +from Globals import InitializeClass
    2.28 +from OFS.SimpleItem import SimpleItem
    2.29 +from Products.PageTemplates.PageTemplateFile import PageTemplateFile
    2.30 +
    2.31 +from Products.CMFCore.utils import UniqueObject
    2.32 +
    2.33 +from permissions import ManagePortal
    2.34 +
    2.35 +class CalendarTool (UniqueObject, SimpleItem):
    2.36 +    """ a calendar tool for encapsualting how calendars work and are displayed """
    2.37 +    id = 'portal_calendar'
    2.38 +    meta_type= 'CMF Calendar Tool'
    2.39 +    security = ClassSecurityInfo()
    2.40 +
    2.41 +    calendar_types = ('Event',)
    2.42 +    calendar_states = ('published',)
    2.43 +    use_session = ''
    2.44 +
    2.45 +    manage_options = ( ({ 'label' : 'Overview', 'action' : 'manage_overview' }
    2.46 +                     ,  { 'label' : 'Configure', 'action' : 'manage_configure' }
    2.47 +                     ,
    2.48 +                     ) + SimpleItem.manage_options
    2.49 +                     )
    2.50 +
    2.51 +    #
    2.52 +    #   ZMI methods
    2.53 +    #
    2.54 +    security.declareProtected( ManagePortal, 'manage_overview' )
    2.55 +    manage_overview = PageTemplateFile('www/explainCalendarTool', globals(),
    2.56 +                                   __name__='manage_overview')
    2.57 +
    2.58 +    security.declareProtected( ManagePortal, 'manage_configure' )
    2.59 +    manage_configure = PageTemplateFile('www/configureCalendarTool', globals(),
    2.60 +                                   __name__='manage_configure')
    2.61 +
    2.62 +    security.declareProtected( ManagePortal, 'edit_configuration' )
    2.63 +    def edit_configuration(self, show_types, use_session, show_states=None):
    2.64 +        """ Change the configuration of the calendar tool """
    2.65 +        self.calendar_types = tuple(show_types)
    2.66 +        self.use_session = use_session
    2.67 +        if show_states is not None:
    2.68 +            self.calendar_states = tuple(show_states)
    2.69 +        if hasattr(self.REQUEST, 'RESPONSE'):
    2.70 +            self.REQUEST.RESPONSE.redirect('manage_configure')
    2.71 +
    2.72 +    security.declarePublic('getCalendarTypes')
    2.73 +    def getCalendarTypes(self):
    2.74 +        """ Returns a list of type that will show in the calendar """
    2.75 +        return self.calendar_types
    2.76 +
    2.77 +    security.declarePublic('getCalendarStates')
    2.78 +    def getCalendarStates(self):
    2.79 +        """ Returns a list of workflow states that will show in the calendar """
    2.80 +        return self.calendar_states
    2.81 +
    2.82 +    security.declarePublic('getUseSession')
    2.83 +    def getUseSession(self):
    2.84 +        """ Returns the Use_Session option """
    2.85 +        return self.use_session
    2.86 +
    2.87 +    security.declarePublic('getDays')
    2.88 +    def getDays(self):
    2.89 +        """ Returns a list of days with the correct start day first """
    2.90 +        return calendar.weekheader(2).split()
    2.91 +
    2.92 +    security.declarePublic('getWeeksList')
    2.93 +    def getWeeksList(self, month='1', year='2002'):
    2.94 +        """Creates a series of weeks, each of which contains an integer day number.
    2.95 +           A day number of 0 means that day is in the previous or next month.
    2.96 +        """
    2.97 +        year=int(year)
    2.98 +        month=int(month)
    2.99 +        # daysByWeek is a list of days inside a list of weeks, like so:
   2.100 +        # [[0, 1, 2, 3, 4, 5, 6],
   2.101 +        #  [7, 8, 9, 10, 11, 12, 13],
   2.102 +        #  [14, 15, 16, 17, 18, 19, 20],
   2.103 +        #  [21, 22, 23, 24, 25, 26, 27],
   2.104 +        #  [28, 29, 30, 31, 0, 0, 0]]
   2.105 +        daysByWeek=calendar.monthcalendar(year, month)
   2.106 +
   2.107 +        return daysByWeek
   2.108 +
   2.109 +    security.declarePublic('getEventsForCalendar')
   2.110 +    def getEventsForCalendar(self, month='1', year='2002'):
   2.111 +        """ recreates a sequence of weeks, by days each day is a mapping.
   2.112 +            {'day': #, 'url': None}
   2.113 +        """
   2.114 +        year=int(year)
   2.115 +        month=int(month)
   2.116 +        # daysByWeek is a list of days inside a list of weeks, like so:
   2.117 +        # [[0, 1, 2, 3, 4, 5, 6],
   2.118 +        #  [7, 8, 9, 10, 11, 12, 13],
   2.119 +        #  [14, 15, 16, 17, 18, 19, 20],
   2.120 +        #  [21, 22, 23, 24, 25, 26, 27],
   2.121 +        #  [28, 29, 30, 31, 0, 0, 0]]
   2.122 +        daysByWeek=calendar.monthcalendar(year, month)
   2.123 +        weeks=[]
   2.124 +
   2.125 +        events=self.catalog_getevents(year, month)
   2.126 +
   2.127 +        for week in daysByWeek:
   2.128 +            days=[]
   2.129 +            for day in week:
   2.130 +                if events.has_key(day):
   2.131 +                    days.append(events[day])
   2.132 +                else:
   2.133 +                    days.append({'day': day, 'event': 0, 'eventslist':[]})
   2.134 +
   2.135 +            weeks.append(days)
   2.136 +
   2.137 +        return weeks
   2.138 +
   2.139 +    security.declarePublic('catalog_getevents')
   2.140 +    def catalog_getevents(self, year, month):
   2.141 +        """ given a year and month return a list of days that have events """
   2.142 +        year=int(year)
   2.143 +        month=int(month)
   2.144 +        last_day=calendar.monthrange(year, month)[1]
   2.145 +        first_date=self.getBeginAndEndTimes(1, month, year)[0]
   2.146 +        last_date=self.getBeginAndEndTimes(last_day, month, year)[1]
   2.147 +
   2.148 +        query = self.portal_catalog(
   2.149 +                        portal_type=self.getCalendarTypes(),
   2.150 +                        review_state=self.getCalendarStates(),
   2.151 +                        start={'query': last_date, 'range': 'max'},
   2.152 +                        end={'query': first_date, 'range': 'min'},
   2.153 +                        sort_on='start' )
   2.154 +
   2.155 +        # compile a list of the days that have events
   2.156 +        eventDays={}
   2.157 +        for daynumber in range(1, 32): # 1 to 31
   2.158 +            eventDays[daynumber] = {'eventslist':[], 'event':0, 'day':daynumber}
   2.159 +        includedevents = []
   2.160 +        for result in query:
   2.161 +            if result.getRID() in includedevents:
   2.162 +                break
   2.163 +            else:
   2.164 +                includedevents.append(result.getRID())
   2.165 +            event={}
   2.166 +            # we need to deal with events that end next month
   2.167 +            if  result.end.month() != month:
   2.168 +                # doesn't work for events that last ~12 months
   2.169 +                # fix it if it's a problem, otherwise ignore
   2.170 +                eventEndDay = last_day
   2.171 +                event['end'] = None
   2.172 +            else:
   2.173 +                eventEndDay = result.end.day()
   2.174 +                event['end'] = result.end.Time()
   2.175 +            # and events that started last month
   2.176 +            if result.start.month() != month:  # same as above re: 12 month thing
   2.177 +                eventStartDay = 1
   2.178 +                event['start'] = None
   2.179 +            else:
   2.180 +                eventStartDay = result.start.day()
   2.181 +                event['start'] = result.start.Time()
   2.182 +
   2.183 +            event['title'] = result.Title or result.getId
   2.184 +
   2.185 +            if eventStartDay != eventEndDay:
   2.186 +                allEventDays = range(eventStartDay, eventEndDay+1)
   2.187 +                eventDays[eventStartDay]['eventslist'].append( {'end': None,
   2.188 +                        'start': result.start.Time(), 'title': event['title']} )
   2.189 +                eventDays[eventStartDay]['event'] = 1
   2.190 +
   2.191 +                for eventday in allEventDays[1:-1]:
   2.192 +                    eventDays[eventday]['eventslist'].append( {'end': None,
   2.193 +                                       'start': None, 'title': event['title']} )
   2.194 +                    eventDays[eventday]['event'] = 1
   2.195 +
   2.196 +                if result.end == result.end.earliestTime():
   2.197 +                    last_day_data = eventDays[allEventDays[-2]]
   2.198 +                    last_days_event = last_day_data['eventslist'][-1]
   2.199 +                    last_days_event['end'] = (result.end-1).latestTime().Time()
   2.200 +                else:
   2.201 +                    eventDays[eventEndDay]['eventslist'].append( {'end':
   2.202 +                        result.end.Time(), 'start': None, 'title': event['title']} )
   2.203 +                    eventDays[eventEndDay]['event'] = 1
   2.204 +            else:
   2.205 +                eventDays[eventStartDay]['eventslist'].append(event)
   2.206 +                eventDays[eventStartDay]['event'] = 1
   2.207 +            # This list is not uniqued and isn't sorted
   2.208 +            # uniquing and sorting only wastes time
   2.209 +            # and in this example we don't need to because
   2.210 +            # later we are going to do an 'if 2 in eventDays'
   2.211 +            # so the order is not important.
   2.212 +            # example:  [23, 28, 29, 30, 31, 23]
   2.213 +        return eventDays
   2.214 +
   2.215 +    security.declarePublic('getEventsForThisDay')
   2.216 +    def getEventsForThisDay(self, thisDay):
   2.217 +        """ given an exact day return ALL events that:
   2.218 +            A) Start on this day  OR
   2.219 +            B) End on this day  OR
   2.220 +            C) Start before this day  AND  end after this day"""
   2.221 +
   2.222 +        catalog = self.portal_catalog
   2.223 +        day, month, year = ( int(thisDay.day())
   2.224 +                           , int(thisDay.month())
   2.225 +                           , int(thisDay.year())
   2.226 +                           )
   2.227 +
   2.228 +        first_date, last_date = self.getBeginAndEndTimes(day, month, year)
   2.229 +        zone = first_date.localZone()
   2.230 +        after_midnight_str = '%d-%02d-%02d 00:01:00 %s' % (year,month,day,zone)
   2.231 +        after_midnight = DateTime(after_midnight_str)
   2.232 +
   2.233 +        # Get all events that Start on this day
   2.234 +        query = self.portal_catalog(
   2.235 +                        portal_type=self.getCalendarTypes(),
   2.236 +                        review_state=self.getCalendarStates(),
   2.237 +                        start={'query': (first_date, last_date),
   2.238 +                               'range': 'minmax'} )
   2.239 +
   2.240 +        # Get all events that End on this day
   2.241 +        query += self.portal_catalog(
   2.242 +                         portal_type=self.getCalendarTypes(),
   2.243 +                         review_state=self.getCalendarStates(),
   2.244 +                         end={'query': (after_midnight, last_date),
   2.245 +                              'range': 'minmax'} )
   2.246 +
   2.247 +        # Get all events that Start before this day AND End after this day
   2.248 +        query += self.portal_catalog(
   2.249 +                         portal_type=self.getCalendarTypes(),
   2.250 +                         review_state=self.getCalendarStates(),
   2.251 +                         start={'query': first_date, 'range': 'max'},
   2.252 +                         end={'query': last_date, 'range': 'min'} )
   2.253 +
   2.254 +        # Unique the results
   2.255 +        results = []
   2.256 +        rids = []
   2.257 +        for item in query:
   2.258 +            rid = item.getRID()
   2.259 +            if not rid in rids:
   2.260 +                results.append(item)
   2.261 +                rids.append(rid)
   2.262 +
   2.263 +        def sort_function(x,y):
   2.264 +            z = cmp(x.start,y.start)
   2.265 +            if not z:
   2.266 +                return cmp(x.end,y.end)
   2.267 +            return z
   2.268 +
   2.269 +        # Sort by start date
   2.270 +        results.sort(sort_function)
   2.271 +
   2.272 +        return results
   2.273 +
   2.274 +    security.declarePublic('getPreviousMonth')
   2.275 +    def getPreviousMonth(self, month, year):
   2.276 +        # given any particular year and month, this method will return a 
   2.277 +        # datetime object for one month prior
   2.278 +
   2.279 +        try: month=int(month)
   2.280 +        except: raise "Calendar Type Error", month
   2.281 +        try: year=int(year)
   2.282 +        except: raise "Calendar Type Error", year
   2.283 +
   2.284 +        if month==0 or month==1:
   2.285 +            month, year = 12, year - 1
   2.286 +        else:
   2.287 +            month-=1
   2.288 +
   2.289 +        return DateTime(year, month, 1)
   2.290 +
   2.291 +    security.declarePublic('getNextMonth')
   2.292 +    def getNextMonth(self, month, year):
   2.293 +        # given any particular year and month, this method will return a datetime object
   2.294 +        # for one month after
   2.295 +
   2.296 +        try: month=int(month)
   2.297 +        except: raise "Calendar Type Error", month
   2.298 +        try: year=int(year)
   2.299 +        except: raise "Calendar Type Error", year
   2.300 +
   2.301 +        if month==12:
   2.302 +            month, year = 1, year + 1
   2.303 +        else:
   2.304 +            month+=1
   2.305 +
   2.306 +        return DateTime(year, month, 1)
   2.307 +
   2.308 +    security.declarePublic('getBeginAndEndTimes')
   2.309 +    def getBeginAndEndTimes(self, day, month, year):
   2.310 +        # Given any day, month and year this method returns 2 DateTime objects
   2.311 +        # That represent the exact start and the exact end of that particular day.
   2.312 +
   2.313 +        day=int(day)
   2.314 +        month=int(month)
   2.315 +        year=int(year)
   2.316 +
   2.317 +        begin=DateTime('%d-%02d-%02d 00:00:00' % (year, month, day))
   2.318 +        end=DateTime('%d-%02d-%02d 23:59:59' % (year, month, day))
   2.319 +
   2.320 +        return (begin, end)
   2.321 +
   2.322 +InitializeClass(CalendarTool)
     3.1 new file mode 100644
     3.2 --- /dev/null
     3.3 +++ b/DEPENDENCIES.txt
     3.4 @@ -0,0 +1,5 @@
     3.5 +Zope >= 2.8.5
     3.6 +Five >= 1.2
     3.7 +CMFCore
     3.8 +CMFDefault
     3.9 +GenericSetup
     4.1 new file mode 100644
     4.2 --- /dev/null
     4.3 +++ b/Event.py
     4.4 @@ -0,0 +1,436 @@
     4.5 +##############################################################################
     4.6 +#
     4.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
     4.8 +#
     4.9 +# This software is subject to the provisions of the Zope Public License,
    4.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    4.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    4.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    4.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    4.14 +# FOR A PARTICULAR PURPOSE.
    4.15 +#
    4.16 +##############################################################################
    4.17 +""" Event: A CMF-enabled Event object.
    4.18 +
    4.19 +$Id: Event.py 36890 2005-04-05 10:48:10Z yuppie $
    4.20 +"""
    4.21 +
    4.22 +from AccessControl import ClassSecurityInfo
    4.23 +from DateTime import DateTime
    4.24 +from Globals import InitializeClass
    4.25 +try:
    4.26 +    import transaction
    4.27 +except ImportError:
    4.28 +    # BBB: for Zope 2.7
    4.29 +    from Products.CMFCore.utils import transaction
    4.30 +
    4.31 +from Products.CMFCore.PortalContent import PortalContent
    4.32 +from Products.CMFCore.utils import contributorsplitter
    4.33 +from Products.CMFCore.utils import keywordsplitter
    4.34 +
    4.35 +from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
    4.36 +from Products.CMFDefault.utils import bodyfinder
    4.37 +from Products.CMFDefault.utils import formatRFC822Headers
    4.38 +from Products.CMFDefault.utils import html_headcheck
    4.39 +from Products.CMFDefault.utils import parseHeadersBody
    4.40 +from Products.CMFDefault.utils import SimpleHTMLParser
    4.41 +
    4.42 +from exceptions import ResourceLockedError
    4.43 +from permissions import ChangeEvents
    4.44 +from permissions import ModifyPortalContent
    4.45 +from permissions import View
    4.46 +
    4.47 +# Factory type information -- makes Events objects play nicely
    4.48 +# with the Types Tool (portal_types)
    4.49 +factory_type_information = (
    4.50 +    {'id': 'Event',
    4.51 +     'icon': 'event_icon.gif',
    4.52 +     'meta_type': 'CMF Event',
    4.53 +     'description': ('Events are objects for use in Calendar topical '
    4.54 +                     'queries on the catalog.'),
    4.55 +     'product': 'CMFCalendar',
    4.56 +     'factory': 'addEvent',
    4.57 +     'immediate_view': 'event_edit_form',
    4.58 +     'actions': ({'id': 'view',
    4.59 +                  'name': 'View',
    4.60 +                  'action': 'string:${object_url}/event_view',
    4.61 +                  'permissions': (View,)},
    4.62 +                 {'id': 'edit',
    4.63 +                  'name': 'Edit',
    4.64 +                  'action': 'string:${object_url}/event_edit_form',
    4.65 +                  'permissions': (ChangeEvents,)},
    4.66 +                 ),                     # End Actions
    4.67 +     },
    4.68 +    )
    4.69 +
    4.70 +def addEvent(self
    4.71 +             , id
    4.72 +             , title=''
    4.73 +             , description=''
    4.74 +             , effective_date = None 
    4.75 +             , expiration_date = None 
    4.76 +             , start_date = None 
    4.77 +             , end_date = None
    4.78 +             , location=''
    4.79 +             , contact_name=''
    4.80 +             , contact_email=''
    4.81 +             , contact_phone=''
    4.82 +             , event_url=''
    4.83 +             , REQUEST=None):
    4.84 +    """
    4.85 +    Create an empty event.
    4.86 +    """
    4.87 +    event = Event(id
    4.88 +                  , title
    4.89 +                  , description
    4.90 +                  , effective_date
    4.91 +                  , expiration_date
    4.92 +                  , start_date
    4.93 +                  , end_date
    4.94 +                  , location
    4.95 +                  , contact_name
    4.96 +                  , contact_email
    4.97 +                  , contact_phone
    4.98 +                  , event_url
    4.99 +                 )
   4.100 +    self._setObject(id, event)
   4.101 +
   4.102 +def _dateStrings( when ):
   4.103 +
   4.104 +    strings = {}
   4.105 +
   4.106 +    if when is not None:
   4.107 +        strings[ 'year' ]   = str( when.year() )
   4.108 +        strings[ 'month' ]  = str( when.month() )
   4.109 +        strings[ 'day' ]    = str( when.day() )
   4.110 +    else:
   4.111 +        strings[ 'year' ]   = ''
   4.112 +        strings[ 'month' ]  = ''
   4.113 +        strings[ 'day' ]    = ''
   4.114 +
   4.115 +    return strings
   4.116 +
   4.117 +class Event(PortalContent, DefaultDublinCoreImpl):
   4.118 +    """
   4.119 +    Events are objects for the Calendar topical query.
   4.120 +    """
   4.121 +    meta_type='CMF Event'
   4.122 +
   4.123 +    # Declarative security
   4.124 +    security = ClassSecurityInfo()
   4.125 +    security.declareObjectProtected(View)
   4.126 +
   4.127 +    __implements__ = ( PortalContent.__implements__
   4.128 +                     , DefaultDublinCoreImpl.__implements__
   4.129 +                     )
   4.130 +
   4.131 +    def __init__(self
   4.132 +                 , id
   4.133 +                 , title=''
   4.134 +                 , description=''
   4.135 +                 , effective_date = None 
   4.136 +                 , expiration_date = None 
   4.137 +                 , start_date = None
   4.138 +                 , end_date = None
   4.139 +                 , location=''
   4.140 +                 , contact_name=''
   4.141 +                 , contact_email=''
   4.142 +                 , contact_phone=''
   4.143 +                 , event_url=''
   4.144 +                ):
   4.145 +        DefaultDublinCoreImpl.__init__(self)
   4.146 +        self.id=id
   4.147 +        self.setTitle(title)
   4.148 +        self.setDescription(description)
   4.149 +        self.effective_date = effective_date
   4.150 +        self.expiration_date = expiration_date
   4.151 +        self.setStartDate(start_date)
   4.152 +
   4.153 +        if start_date is None:
   4.154 +            start_date = DateTime()
   4.155 +        if end_date is None:
   4.156 +            end_date = start_date
   4.157 +
   4.158 +        if end_date < start_date:
   4.159 +            end_date = start_date
   4.160 +
   4.161 +        self.setEndDate(end_date)
   4.162 +        self.location=location
   4.163 +        self.contact_name=contact_name
   4.164 +        self.contact_email=contact_email
   4.165 +        self.contact_phone=contact_phone
   4.166 +        self.event_url=event_url
   4.167 +
   4.168 +    security.declarePrivate( '_datify' )
   4.169 +    def _datify( self, attrib ):
   4.170 +        if attrib == 'None':
   4.171 +            attrib = None
   4.172 +        elif not isinstance( attrib, DateTime ):
   4.173 +            if attrib is not None:
   4.174 +                attrib = DateTime( attrib )
   4.175 +        return attrib
   4.176 +
   4.177 +    security.declarePublic('getEndStrings')
   4.178 +    def getEndStrings(self):
   4.179 +        """
   4.180 +        """
   4.181 +        return _dateStrings(self.end())
   4.182 +
   4.183 +    security.declarePublic('getStartStrings')
   4.184 +    def getStartStrings(self):
   4.185 +        """
   4.186 +        """
   4.187 +        return _dateStrings(self.start())
   4.188 +
   4.189 +    security.declareProtected(ChangeEvents, 'edit')
   4.190 +    def edit(self
   4.191 +             , title=None
   4.192 +             , description=None
   4.193 +             , eventType=None
   4.194 +             , effectiveDay=None
   4.195 +             , effectiveMo=None
   4.196 +             , effectiveYear=None
   4.197 +             , expirationDay=None
   4.198 +             , expirationMo=None
   4.199 +             , expirationYear=None
   4.200 +             , start_time=None
   4.201 +             , startAMPM=None
   4.202 +             , stop_time=None
   4.203 +             , stopAMPM=None
   4.204 +             , location=None
   4.205 +             , contact_name=None
   4.206 +             , contact_email=None
   4.207 +             , contact_phone=None
   4.208 +             , event_url=None
   4.209 +            ):
   4.210 +        """\
   4.211 +        """
   4.212 +
   4.213 +        if title is not None: 
   4.214 +            self.setTitle(title)
   4.215 +        if description is not None:
   4.216 +            self.setDescription(description)
   4.217 +        if eventType is not None:
   4.218 +            self.setSubject(eventType)
   4.219 +
   4.220 +        start_date = end_date = None
   4.221 +
   4.222 +        if effectiveDay and effectiveMo and effectiveYear and start_time:
   4.223 +            efdate = '%s/%s/%s %s %s' % (effectiveYear
   4.224 +                                         , effectiveMo
   4.225 +                                         , effectiveDay
   4.226 +                                         , start_time
   4.227 +                                         , startAMPM
   4.228 +                                         )
   4.229 +            start_date = DateTime( efdate )
   4.230 +
   4.231 +        if expirationDay and expirationMo and expirationYear and stop_time:
   4.232 +
   4.233 +            exdate = '%s/%s/%s %s %s' % (expirationYear
   4.234 +                                         , expirationMo
   4.235 +                                         , expirationDay
   4.236 +                                         , stop_time
   4.237 +                                         , stopAMPM
   4.238 +                                         )
   4.239 +            end_date = DateTime( exdate )
   4.240 +
   4.241 +        if start_date and end_date:
   4.242 +
   4.243 +            if end_date < start_date:
   4.244 +                end_date = start_date
   4.245 +
   4.246 +            self.setStartDate( start_date )
   4.247 +            self.setEndDate( end_date )
   4.248 +
   4.249 +        if location is not None:
   4.250 +            self.location = location
   4.251 +        if contact_name is not None:
   4.252 +            self.contact_name = contact_name
   4.253 +        if contact_email is not None:
   4.254 +            self.contact_email = contact_email
   4.255 +        if contact_phone is not None:
   4.256 +            self.contact_phone = contact_phone
   4.257 +        if event_url is not None:
   4.258 +            self.event_url = event_url
   4.259 +        self.reindexObject()
   4.260 +
   4.261 +    security.declarePublic('buildTimes')
   4.262 +    def buildTimes(self):
   4.263 +        result = []
   4.264 +        for hour in range (1, 13):
   4.265 +            for min in (00, 30):
   4.266 +                result.append('%02d:%02d' % (hour, min))
   4.267 +        return result
   4.268 +
   4.269 +    security.declarePublic('buildDays')
   4.270 +    def buildDays(self):
   4.271 +        result = []
   4.272 +        for day in range (1, 32):
   4.273 +            result.append(str('%d' % (day)))
   4.274 +        return result
   4.275 +
   4.276 +    security.declarePublic('buildMonths')
   4.277 +    def buildMonths(self):
   4.278 +        result = []
   4.279 +        for month in range (1, 13):
   4.280 +            result.append(str('%d' % (month)))
   4.281 +        return result
   4.282 +
   4.283 +    security.declarePublic('buildYears')
   4.284 +    def buildYears(self):
   4.285 +        result = []
   4.286 +        start = (DateTime().year() - 2)
   4.287 +        end = (DateTime().year() + 5)
   4.288 +        for year in range (start, end):
   4.289 +            result.append(str(year))
   4.290 +        return result
   4.291 +
   4.292 +    security.declareProtected(ChangeEvents, 'setStartDate')
   4.293 +    def setStartDate(self, start):
   4.294 +        """
   4.295 +        Setting the event start date, when the event is scheduled to begin.
   4.296 +        """
   4.297 +        self.start_date = self._datify(start)
   4.298 +
   4.299 +    security.declareProtected(ChangeEvents, 'setEndDate')
   4.300 +    def setEndDate(self, end):
   4.301 +        """
   4.302 +        Setting the event end date, when the event ends.
   4.303 +        """
   4.304 +        self.end_date = self._datify(end)
   4.305 +
   4.306 +    security.declarePublic('start')
   4.307 +    def start(self):
   4.308 +        """
   4.309 +            Return our start time as a string.
   4.310 +        """
   4.311 +        date = getattr( self, 'start_date', None )
   4.312 +        return date is None and self.created() or date
   4.313 +
   4.314 +    security.declarePublic('end')
   4.315 +    def end(self):
   4.316 +        """
   4.317 +            Return our stop time as a string.
   4.318 +        """
   4.319 +        date = getattr( self, 'end_date', None )
   4.320 +        return date is None and self.start() or date    
   4.321 +
   4.322 +    security.declarePublic('getStartTimeString')
   4.323 +    def getStartTimeString( self ):
   4.324 +        """
   4.325 +            Return our start time as a string.
   4.326 +        """
   4.327 +        return self.start().AMPMMinutes() 
   4.328 +
   4.329 +    security.declarePublic('getStopTimeString')
   4.330 +    def getStopTimeString( self ):
   4.331 +        """
   4.332 +            Return our stop time as a string.
   4.333 +        """
   4.334 +        return self.end().AMPMMinutes() 
   4.335 +
   4.336 +    security.declarePrivate('handleText')
   4.337 +    def handleText(self, text, format=None):
   4.338 +        """ Handles the raw text, returning headers, body, cooked, format """
   4.339 +        headers = {}
   4.340 +        if format == 'html':
   4.341 +            parser = SimpleHTMLParser()
   4.342 +            parser.feed(text)
   4.343 +            headers.update(parser.metatags)
   4.344 +            if parser.title:
   4.345 +                headers['Title'] = parser.title
   4.346 +            bodyfound = bodyfinder(text)
   4.347 +            if bodyfound:
   4.348 +                body = bodyfound
   4.349 +        else:
   4.350 +            headers, body = parseHeadersBody(text, headers)
   4.351 +
   4.352 +        return headers, body, format
   4.353 +
   4.354 +    security.declareProtected(ModifyPortalContent, 'setMetadata')
   4.355 +    def setMetadata(self, headers):
   4.356 +        headers['Format'] = self.Format()
   4.357 +        new_subject = keywordsplitter(headers)
   4.358 +        headers['Subject'] = new_subject or self.Subject()
   4.359 +        new_contrib = contributorsplitter(headers)
   4.360 +        headers['Contributors'] = new_contrib or self.Contributors()
   4.361 +        haveheader = headers.has_key
   4.362 +        for key, value in self.getMetadataHeaders():
   4.363 +            if not haveheader(key):
   4.364 +                headers[key] = value
   4.365 +        self._editMetadata(title=headers['Title'],
   4.366 +                          subject=headers['Subject'],
   4.367 +                          description=headers['Description'],
   4.368 +                          contributors=headers['Contributors'],
   4.369 +                          effective_date=headers['Effective_date'],
   4.370 +                          expiration_date=headers['Expiration_date'],
   4.371 +                          format=headers['Format'],
   4.372 +                          language=headers['Language'],
   4.373 +                          rights=headers['Rights'],
   4.374 +                          )
   4.375 +
   4.376 +    security.declarePublic( 'getMetadataHeaders' )
   4.377 +    def getMetadataHeaders(self):
   4.378 +        """Return RFC-822-style header spec."""
   4.379 +        hdrlist = DefaultDublinCoreImpl.getMetadataHeaders(self)
   4.380 +        hdrlist.append( ('StartDate', self.start().strftime("%Y-%m-%d %H:%M:%S") ) )
   4.381 +        hdrlist.append( ('EndDate',  self.end().strftime("%Y-%m-%d %H:%M:%S") ) )
   4.382 +        hdrlist.append( ('Location', self.location) )
   4.383 +        hdrlist.append( ('ContactName', self.contact_name) )
   4.384 +        hdrlist.append( ('ContactEmail', self.contact_email) )
   4.385 +        hdrlist.append( ('ContactPhone', self.contact_phone) )
   4.386 +        hdrlist.append( ('EventURL', self.event_url) )
   4.387 +
   4.388 +        return hdrlist
   4.389 +
   4.390 +    ## FTP handlers
   4.391 +    security.declareProtected(ModifyPortalContent, 'PUT')
   4.392 +
   4.393 +    def PUT(self, REQUEST, RESPONSE):
   4.394 +        """ Handle HTTP (and presumably FTP?) PUT requests """
   4.395 +        self.dav__init(REQUEST, RESPONSE)
   4.396 +        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
   4.397 +        body = REQUEST.get('BODY', '')
   4.398 +        guessedformat = REQUEST.get_header('Content-Type', 'text/plain')
   4.399 +        ishtml = (guessedformat == 'text/html') or html_headcheck(body)
   4.400 +
   4.401 +        if ishtml: self.setFormat('text/html')
   4.402 +        else: self.setFormat('text/plain')
   4.403 +
   4.404 +        try:
   4.405 +            headers, body, format = self.handleText(text=body)
   4.406 +            self.setMetadata(headers)
   4.407 +            self.setStartDate(headers['StartDate'])
   4.408 +            self.setEndDate(headers['EndDate'])
   4.409 +            self.edit( location=headers['Location']
   4.410 +             , contact_name=headers['ContactName']
   4.411 +             , contact_email=headers['ContactEmail']
   4.412 +             , contact_phone=headers['ContactPhone']
   4.413 +             , event_url=headers['EventURL']
   4.414 +             )
   4.415 +
   4.416 +        except ResourceLockedError, msg:
   4.417 +            transaction.abort()
   4.418 +            RESPONSE.setStatus(423)
   4.419 +            return RESPONSE
   4.420 +
   4.421 +        RESPONSE.setStatus(204)
   4.422 +        self.reindexObject()
   4.423 +        return RESPONSE
   4.424 +
   4.425 +    security.declareProtected(View, 'manage_FTPget')
   4.426 +    def manage_FTPget(self):
   4.427 +        "Get the document body for FTP download (also used for the WebDAV SRC)"
   4.428 +        hdrlist = self.getMetadataHeaders()
   4.429 +        hdrtext = formatRFC822Headers( hdrlist )
   4.430 +        bodytext = '%s\r\n\r\n%s' % ( hdrtext, self.Description() )
   4.431 +
   4.432 +        return bodytext
   4.433 +
   4.434 +    security.declareProtected(View, 'get_size')
   4.435 +    def get_size( self ):
   4.436 +        """ Used for FTP and apparently the ZMI now too """
   4.437 +        return len(self.manage_FTPget())
   4.438 +
   4.439 +# Intialize the Event class, setting up security.
   4.440 +InitializeClass(Event)
     5.1 new file mode 100644
     5.2 --- /dev/null
     5.3 +++ b/EventPermissions.py
     5.4 @@ -0,0 +1,24 @@
     5.5 +##############################################################################
     5.6 +#
     5.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
     5.8 +#
     5.9 +# This software is subject to the provisions of the Zope Public License,
    5.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    5.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    5.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    5.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    5.14 +# FOR A PARTICULAR PURPOSE.
    5.15 +#
    5.16 +##############################################################################
    5.17 +""" Backward compatibility;  see Products.CMFCalendar.permissions
    5.18 +
    5.19 +$Id: EventPermissions.py 36457 2004-08-12 15:07:44Z jens $
    5.20 +"""
    5.21 +
    5.22 +from permissions import *
    5.23 +
    5.24 +from warnings import warn
    5.25 +
    5.26 +warn( "The module, 'Products.CMFCalendar.EventPermissions' is a deprecated "
    5.27 +      "compatiblity alias for 'Products.CMFalendar.permissions';  please use "
    5.28 +      "the new module instead.", DeprecationWarning)
     6.1 new file mode 100644
     6.2 --- /dev/null
     6.3 +++ b/Extensions/Install.py
     6.4 @@ -0,0 +1,138 @@
     6.5 +##############################################################################
     6.6 +#
     6.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
     6.8 +#
     6.9 +# This software is subject to the provisions of the Zope Public License,
    6.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
    6.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
    6.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    6.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    6.14 +# FOR A PARTICULAR PURPOSE.
    6.15 +#
    6.16 +##############################################################################
    6.17 +"""\
    6.18 +This file is an installation script for CMFCalendar (Events).  It's meant to be
    6.19 +used as an External Method.  To use, add an external method to the
    6.20 +root of the CMF Site that you want CMF Event registered in with the
    6.21 +configuration:
    6.22 +
    6.23 + id: install_events
    6.24 + title: Install Events *optional*
    6.25 + module name: CMFCalendar.Install
    6.26 + function name: install
    6.27 +
    6.28 +Then go to the management screen for the newly added external method
    6.29 +and click the 'Try it' tab.  The install function will execute and give
    6.30 +information about the steps it took to register and install the
    6.31 +CMF Events into the CMF Site instance.
    6.32 +"""
    6.33 +
    6.34 +from cStringIO import StringIO
    6.35 +
    6.36 +from Products.CMFCore.TypesTool import ContentFactoryMetadata
    6.37 +from Products.CMFCore.DirectoryView import addDirectoryViews
    6.38 +from Products.CMFCore.utils import getToolByName
    6.39 +
    6.40 +from Products.CMFCalendar import Event, event_globals
    6.41 +from Products.CMFCalendar.exceptions import CatalogError
    6.42 +from Products.CMFCalendar.exceptions import MetadataError
    6.43 +
    6.44 +def install(self):
    6.45 +    " Register the CMF Event with portal_types and friends "
    6.46 +    out = StringIO()
    6.47 +    typestool = getToolByName(self, 'portal_types')
    6.48 +    skinstool = getToolByName(self, 'portal_skins')
    6.49 +    metadatatool = getToolByName(self, 'portal_metadata')
    6.50 +    ctool = getToolByName(self, 'portal_catalog')
    6.51 +    portal_url = getToolByName(self, 'portal_url')
    6.52 +
    6.53 +    # Set up a catalog indexes and metadata
    6.54 +    try:
    6.55 +        ctool.addIndex('start', 'DateIndex')
    6.56 +    except CatalogError:
    6.57 +        pass
    6.58 +    try:
    6.59 +        ctool.addIndex('end', 'DateIndex')
    6.60 +    except CatalogError:
    6.61 +        pass
    6.62 +    try:
    6.63 +        ctool.addColumn('start')
    6.64 +    except CatalogError:
    6.65 +        pass
    6.66 +    try:
    6.67 +        ctool.addColumn('end')
    6.68 +    except CatalogError:
    6.69 +        pass
    6.70 +    out.write('Added "start" and "end" date indexes and columns to '\
    6.71 +              'the portal_catalog\n')
    6.72 +
    6.73 +    # Borrowed from CMFDefault.Portal.PortalGenerator.setupTypes()
    6.74 +    # We loop through anything defined in the factory type information
    6.75 +    # and configure it in the types tool if it doesn't already exist
    6.76 +    for t in Event.factory_type_information:
    6.77 +        if t['id'] not in typestool.objectIds():
    6.78 +            cfm = ContentFactoryMetadata(**t)
    6.79 +            typestool._setObject(t['id'], cfm)
    6.80 +            out.write('Registered with the types tool\n')
    6.81 +        else:
    6.82 +            out.write('Object "%s" already existed in the types tool\n' % (
    6.83 +                t['id']))
    6.84 +
    6.85 +    # Set up a MetadataTool element policy for events
    6.86 +    try:
    6.87 +        metadatatool.addElementPolicy(
    6.88 +            element='Subject',
    6.89 +            content_type='Event',
    6.90 +            is_required=0,
    6.91 +            supply_default=0,
    6.92 +            default_value='',
    6.93 +            enforce_vocabulary=0,
    6.94 +            allowed_vocabulary=('Appointment', 'Convention', 'Meeting',
    6.95 +                                'Social Event', 'Work'),
    6.96 +            REQUEST=None)
    6.97 +    except MetadataError:
    6.98 +        pass
    6.99 +    out.write('Event added to Metadata element Policies\n')
   6.100 +
   6.101 +    # Add the CMFCalendar tool to the site's root
   6.102 +    p = portal_url.getPortalObject()
   6.103 +    x = p.manage_addProduct['CMFCalendar'].manage_addTool(type="CMF Calendar Tool")
   6.104 +
   6.105 +    # Setup the skins
   6.106 +    # This is borrowed from CMFDefault/scripts/addImagesToSkinPaths.pys
   6.107 +    if 'zpt_calendar' not in skinstool.objectIds():
   6.108 +        # We need to add Filesystem Directory Views for any directories
   6.109 +        # in our skins/ directory.  These directories should already be
   6.110 +        # configured.
   6.111 +        addDirectoryViews(skinstool, 'skins', event_globals)
   6.112 +        out.write("Added '*calendar' directory views to portal_skins\n")
   6.113 +
   6.114 +    # Now we need to go through the skin configurations and insert
   6.115 +    # 'calendar' into the configurations.  Preferably, this should be
   6.116 +    # right before where 'content' is placed.  Otherwise, we append
   6.117 +    # it to the end.
   6.118 +    skins = skinstool.getSkinSelections()
   6.119 +    for skin in skins:
   6.120 +        path = skinstool.getSkinPath(skin)
   6.121 +        path = [ id.strip() for id in path.split(',') ]
   6.122 +        if 'calendar' not in path and 'zpt_calendar' not in path:
   6.123 +            try:
   6.124 +                path.insert(path.index('content'), 'calendar')
   6.125 +                out.write("Added 'calendar' to %s skin\n" % skin)
   6.126 +            except ValueError:
   6.127 +                pass
   6.128 +
   6.129 +            try:
   6.130 +                path.insert(path.index('zpt_content'), 'zpt_calendar')
   6.131 +                out.write("Added 'zpt_calendar' to %s skin\n" % skin)
   6.132 +            except ValueError:
   6.133 +                pass
   6.134 +
   6.135 +            path = ', '.join(path)
   6.136 +            # addSkinSelection will replace exissting skins as well.
   6.137 +            skinstool.addSkinSelection(skin, path)
   6.138 +        else:
   6.139 +            out.write("Skipping %s skin, 'zpt_calendar' is already set up\n" % (
   6.140 +                skin))
   6.141 +
   6.142 +    return out.getvalue()
     7.1 new file mode 100644
     7.2 --- /dev/null
     7.3 +++ b/Extensions/__init__.py
     7.4 @@ -0,0 +1,2 @@
     7.5 +# this file is here to make Install.py importable.
     7.6 +# we need to make it non-zero size to make winzip cooperate
     8.1 new file mode 100644
     8.2 --- /dev/null
     8.3 +++ b/INSTALL.txt
     8.4 @@ -0,0 +1,28 @@
     8.5 +Installing CMFCalendar
     8.6 +
     8.7 +  To install CMFCalendar, uncompress the CMFCalendar product into
     8.8 +  your zope/Products directory or link it there, e.g.::
     8.9 +
    8.10 +    ln -s /path/to/installation /path/to/zope/Products
    8.11 +
    8.12 +  In the root of your CMFSite installation (within the ZMI):
    8.13 +
    8.14 +      1.  Add an external method to the root of the CMF Site.
    8.15 +
    8.16 +      2.  Use the following configuration values for the external
    8.17 +          method:
    8.18 +
    8.19 +          o id: install_events
    8.20 +
    8.21 +          o title: Install Events *optional*
    8.22 +
    8.23 +          o module name: CMFCalendar.Install
    8.24 +
    8.25 +          o function name: install
    8.26 +
    8.27 +      3. Go to the management screen for the newly added external
    8.28 +         method and click the 'Try it' tab.
    8.29 +
    8.30 +  The install function will execute and give information about the
    8.31 +  steps it took to register and install the CMF Events into the CMF
    8.32 +  Site instance.
     9.1 new file mode 100644
     9.2 --- /dev/null
     9.3 +++ b/README.txt
     9.4 @@ -0,0 +1,25 @@
     9.5 +CMFCalendar README
     9.6 +
     9.7 +  The CMFCalendar product is an example of creating a CMF
     9.8 +  Product.  The CMFCalendar product is also expected to be a
     9.9 +  generally useful out of the 'box' and skinnable to accomodate
    9.10 +  customization within your existing CMF instance.  To see how to
    9.11 +  go about building a CMF product, this hopefully allows a
    9.12 +  developer to follow through the process of registering their
    9.13 +  product, skins, and help with the CMF by example.  It shows how
    9.14 +  an object is created and registered with the types_tool,
    9.15 +  necessary skins added to the skins_tool, with the Calendar
    9.16 +  skins directory added to the skin paths, and providing
    9.17 +  portal_metadatool with an Element policy for the content_type
    9.18 +  of the object.
    9.19 +
    9.20 +  The event portal_type is complete, and can be stand alone, but
    9.21 +  now accompanyied by the CMFCalendar which is fully functional
    9.22 +  and tested.
    9.23 +
    9.24 +  After installing the CMFCalendar you should notice a calendar
    9.25 +  appear in your CMF.  This is fully customisable to your portals
    9.26 +  needs.
    9.27 +
    9.28 +  See the INSTALL.txt file for how to get the product installed
    9.29 +  within your CMF.
    10.1 new file mode 100644
    10.2 --- /dev/null
    10.3 +++ b/TODO.txt
    10.4 @@ -0,0 +1,6 @@
    10.5 +To-do's
    10.6 +
    10.7 + o Add the calendar product itself, at rev 0.1 there is only an
    10.8 +   event object. -- Done (Andy Dawkins - 1st May 2002)
    10.9 +
   10.10 + o other todo items as I think of them....
    11.1 new file mode 100644
    11.2 --- /dev/null
    11.3 +++ b/__init__.py
    11.4 @@ -0,0 +1,66 @@
    11.5 +##############################################################################
    11.6 +#
    11.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
    11.8 +#
    11.9 +# This software is subject to the provisions of the Zope Public License,
   11.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   11.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   11.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   11.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   11.14 +# FOR A PARTICULAR PURPOSE.
   11.15 +#
   11.16 +##############################################################################
   11.17 +""" CMF Calendar product.
   11.18 +
   11.19 +$Id: __init__.py 40635 2005-12-07 21:12:32Z tseaver $
   11.20 +"""
   11.21 +
   11.22 +import sys
   11.23 +
   11.24 +from Products.CMFCore import utils
   11.25 +from Products.CMFCore.interfaces import ISiteRoot
   11.26 +from Products.CMFCore.DirectoryView import registerDirectory
   11.27 +from Products.GenericSetup import EXTENSION
   11.28 +from Products.GenericSetup import profile_registry
   11.29 +
   11.30 +import Event
   11.31 +import CalendarTool
   11.32 +from permissions import AddPortalContent
   11.33 +
   11.34 +
   11.35 +this_module = sys.modules[ __name__ ]
   11.36 +
   11.37 +contentConstructors = (Event.addEvent,)
   11.38 +contentClasses = (Event.Event,)
   11.39 +
   11.40 +tools = ( CalendarTool.CalendarTool, )
   11.41 +
   11.42 +z_bases = utils.initializeBasesPhase1( contentClasses, this_module )
   11.43 +
   11.44 +# This is used by a script (external method) that can be run
   11.45 +# to set up Events in an existing CMF Site instance.
   11.46 +event_globals=globals()
   11.47 +
   11.48 +# Make the skins available as DirectoryViews
   11.49 +registerDirectory('skins', globals())
   11.50 +
   11.51 +def initialize( context ):
   11.52 +    utils.ToolInit('CMF Calendar Tool', tools=tools, icon='tool.gif',
   11.53 +                   ).initialize( context )
   11.54 +
   11.55 +    utils.initializeBasesPhase2( z_bases, context )
   11.56 +    utils.ContentInit( 'CMF Event'
   11.57 +                     , content_types = contentClasses
   11.58 +                     , permission = AddPortalContent
   11.59 +                     , extra_constructors = contentConstructors
   11.60 +                     , fti = Event.factory_type_information
   11.61 +                     ).initialize( context )
   11.62 +
   11.63 +    profile_registry.registerProfile('default',
   11.64 +                                     'CMFCalendar',
   11.65 +                                     'Adds calendar support.',
   11.66 +                                     'profiles/default',
   11.67 +                                     'CMFCalendar',
   11.68 +                                     EXTENSION,
   11.69 +                                     for_=ISiteRoot,
   11.70 +                                    )
    12.1 new file mode 100644
    12.2 --- /dev/null
    12.3 +++ b/exceptions.py
    12.4 @@ -0,0 +1,28 @@
    12.5 +##############################################################################
    12.6 +#
    12.7 +# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
    12.8 +#
    12.9 +# This software is subject to the provisions of the Zope Public License,
   12.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   12.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   12.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   12.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   12.14 +# FOR A PARTICULAR PURPOSE.
   12.15 +#
   12.16 +##############################################################################
   12.17 +""" CMFCalendar product exceptions.
   12.18 +
   12.19 +$Id: exceptions.py 36892 2005-04-05 12:21:51Z yuppie $
   12.20 +"""
   12.21 +
   12.22 +from AccessControl import ModuleSecurityInfo
   12.23 +security = ModuleSecurityInfo('Products.CMFCalendar.exceptions')
   12.24 +
   12.25 +security.declarePublic('CatalogError')
   12.26 +from Products.ZCatalog.Catalog import CatalogError
   12.27 +
   12.28 +security.declarePublic('MetadataError')
   12.29 +from Products.CMFDefault.exceptions import MetadataError
   12.30 +
   12.31 +security.declarePublic('ResourceLockedError')
   12.32 +from Products.CMFCore.exceptions import ResourceLockedError
    13.1 new file mode 100644
    13.2 index 0000000000000000000000000000000000000000..c48d79670d782b93c8eb5f8828e08cb01bafc80a
    13.3 GIT binary patch
    13.4 literal 31921
    13.5 zc%1Eg3w%>W_W#@@&^Gh~1*@{Gy9PxCf2@S^Dxz!Ow6tJp%lp+dxotvk9!+kVN0TOL
    13.6 z(o)`qmWL=x0R;qw^}UJ<_*xfKPyw+;0o`4}T^E&#K-<j!%-owaEv@K2{`=egk@n`!
    13.7 zJ#*&FIcLuK&dg166H{{Y5rc%66pkC=n2n-o8D9P0NleL_EIlXBHR}w#BFj*0BxF0S
    13.8 z&xAjCNlr+H;xqU&$z@4~bYice(l#XNTuXb<z`-D?l(b~->Idaezw}iYjef?|p=-RT
    13.9 zHebo}Sc{Rf@;ak|5aVc1M4osiTr&B<jG2mwoUus7DROirT3%-;Q6w8mIF_`L5j!d0
   13.10 z&jxhxC3C!vw`#&1{b_#}`Uuu%aH<juP-8;cFi7aBZ>rJcFq1wgk5EPH?uv__q%x@t
   13.11 ztc6u{7J}qq`z5rO?`}>taz?WvQ^ghYMgyzJXL+Kth5B+taWU|7@ZMYOTA(yOv>{AM
   13.12 zpzpKcJwzEp-+$FXp*>v^dnz!i43=Wn9NCizA>8dtdX@<I=hsmWqCPn)2T_Dq@x=KO
   13.13 zB&BpotU#Y=Hd;;8YtfOBAdBuj(W=w179k3#*Ck29A#(guJ}oatVJv6OW|2`At{c=l
   13.14 zpVO&@*1E8dgqM&9gu4K-$bxu5`c<z;+HA4iHD0vu{{6F3Qx($;ysD&x)r7)^5hE)8
   13.15 zX2I^PG{r>Lz?xM&3|ETE!ixIKDOc=`Q&^i~hR&cd+AN9!tyN!SQ0X{e86j#WYQ%tV
   13.16 zZpsDv4QL=@@Ckex#ZAh{%0P^S5%LuO4fRdd7>ih>@shT<83j;mbaZq~bZksaZ2vy;
   13.17 zKK*a(7aQB}#(_87aN`X(4D26EU*f6z7bEY}N8VQ+*SBw6eBZu(<H=j!c%e!EA0q(o
   13.18 zNByJ0fZ0;!HYDlKNc%JR6L3Ip{08#?XxWRQFVU}3Fh|y_cT{wYybsg0l0gz_s4@;Q
   13.19 zGDa$qNqR-~j_xIm9R-#BrLw^TM)pdcs=DE}GG9XP_*IR+Nx6N<jd=%(MvXRCy`LI&
   13.20 z$LjpA&wQh{jJav^GyXf%>I*dKuWjMS-u%I|&)9FD4ZQxL_45lEcQtISes0YhNB`LJ
   13.21 z`15Zb`_p&Ri_2@)KJmg^$NzkB+{76rwngi<z4-f&zWBaBl1M;X8PzGOcds#2g~1~S
   13.22 z$UuX#+ivKU;9C_>G&qp={@0_16rC|wtxnCqQEeIh%^kgo0#SF4dF=yGV$02%^l4*x
   13.23 z_Oq}GJ!^1RhYGj}#Y$<P{m}#H+`Ze5M`@Z)|Gxi<4^js`kXYHeufqK9{WE=kyvdSv
   13.24 z`@Hyj9?N?Fo!b@_e0R$e&mME%^uqS2qn2BK9(A_$?dHEtZQuRpZ6EDCsGm72_Kn{i
   13.25 z-0|jH)^#`6uSlJEVXXQ}h0V}>{yWc}#fGR!+qM^dRQ!9jm0Npi>b>%B%AL!e<Xc{K
   13.26 z?Y#fiOHbBK>enoNbMUc4j|}?Qvck6|Z))k5LGNv;UcRcj)X-YG;rox5yt8oLl#eQR
   13.27 zKl8AG{o7NmW9J1IXFqY%gi(!uT;|k0b7A8&-!gP-X2ysQKDjdQ(^L7Ef4_C>@GlSV
   13.28 zdH(t54_kh>bau>Lr;luD)!1LV_1GC(+vx+}%{n*h%L(p^^5hfk_a_?ORBxV|+Wg`F
   13.29 zymHgd%I5lY(^LOy_<}37jZ*I$^~vRP!&FDlkAC>g>4TUiLsPl5^>^Ej<lMXWn<pzD
   13.30 zZTfakaQBDn<{5sucFy|OjvQ$%t!Q33XX95v%ek3{zrNT$X2v})j+1U%uqwOk<?7c?
   13.31 zJy&pC{`m6Brs0(n2E8>csJ=g~;p}JDom%@d>-4v;I)TxwofX0I)4_AWQqKctJb!!O
   13.32 z)=Rg2He^NoxCyWC-MrL;kMPSf_Kp~q760o;fBo)DZ#-xnaqRRrf8P7sAq`g+x12Vt
   13.33 zUp~(6xbv5RbBDEmp0RH0>c{eJ+oEhMp8c%x%D&a#wye(o^46WdXUB3U9iM#am_Kv%
   13.34 zB=@X4{xodJHvF-GI)evIXU!IvR26EY-lXDnMLJH$I}|!`)}JOI6hi$_GEC=2R0Pnd
   13.35 zK)FZ@WgZR-Ex{CmZny@A0*yuYASDXTq5U@9l*?I5ex)-g%rLiG&1zOr#2MA41n{c#
   13.36 zMiY;O^+_n7qq>4G>X;istBtE(WX>UHS*Xy{nU=2~*N^MR|JkF{i(*vc4`9?*y@lmP
   13.37 zW^pA-$ObD1>oJO4v6%&vb3!Za2a6V}ui_%@<jFj5))iTKHWik8oY7Ds3LP-jYUJ5u
   13.38 zjtetbii}3m4E4)5snmenfZn`Lt>VO1M0kI(D${7zIgPN|jI4~!=N%kt$uOGrDn6pC
   13.39 zepv=J2fLtDqh8M%c-jx)kDF-B(UlW2og6qeq(G>bbiIjpq_P}miI65QNSmS4@Y+n4
   13.40 zY>*<m&(f<(*vTqWfx{GOX<Ue&V^pii9#5<zo5}$hI*!d(m4gp|(3R0tPRI;#a~87W
   13.41 zXK7XwEc{`sL8MJl>Dd%k3o?!72<_;eE=8p-ErGp<K@)1*f0~7*`sG`TijACxHHT^j
   13.42 zbS4wA1?e*j5SgoVNRL#ggzhBB>t?#~2=z`J3L8G~^mHzVF!T<2Lp<Lgo@2!Gz^-R<
   13.43 z2wv$?W~ePAp5sGa?b1i@p862aHxVa<dI+x(x*Y@(_3{qZ$trc&iyc&P`x2_@+fg%Z
   13.44 zO4iH}IjTTa(j_7K<me1+x<Nx?AcEXG%b)?X2}PXEd&7{W=I4ik^(K;s9RvG=VMKO>
   13.45 z@u3k<wsZ~$!WiE{qI+t(fsKfwuJsemEE|evqT7Hj^?9r&qV|R^wJDsH?Y^2Sn5*Ko
   13.46 zV#o<*M2YLU4)@G~#o3&>!X8+B2|HaGb=7oAnNFKerwKQmAfEvc0;8d%?yT4fE=Y@O
   13.47 zA5p?ib}W&@Sq)^L9Zz#22QtC|8DXZ2L5i@P!D1%f=Tr_ey>{rxI*w=?L$1|a5%Ic=
   13.48 zs7J10VQNPaJNi;XsWI}JvzUrIU8^}W>T(aE*ls2Z*0}w*2(<mfQP)?bhc;5mM(UxB
   13.49 z)I%FPd#Lx{>!C4J!(w{^C_Gn`(J_;th@3+CYL47G$hbY%tWKI0*0Zl(W#L(K#>{-1
   13.50 zj#q0#xr5lEBCb<-Qwp4Jre%YwD9oxY;U=>NtDcloIKyP2H?u*kOYJNsZ(>SnNT)oF
   13.51 zLZLBQi;|3|!wO9_8?K_Ia98tz<Ojp{<n0gBEgIWZ+DztnlUVm7w22(XkT#LDkT#Jt
   13.52 zkld{{Mde&UNE=e7z9~-05wG*Znx^MdY{)X0Q^?s`!j*&_%&AH^Dm>Xxk~F`g(;OA2
   13.53 z3DbWap^UjCFRaW19rPm3Y8L2rE@@x{y_`#nP{blAqEL`?f!UDX*=6}!6LA>{V>Cst
   13.54 zP=qI}9GJ2Uken)H7C2MQ<qB~ok#v@kYR=_DGBV9hLsxfBH}mR9+O=CphDNA$1g)>i
   13.55 z!0S|;PGy<M87<*e5R3(H2)QJp|7fd)&DW|-Y<LVNn@av0{D6`Td~(?OEQ84$4wS4g
   13.56 zj~uKoVC}rs9A2$aoB@=8zLyarDYEJf`KV)8O-LHZ1GpMRwdy5`BXHXbMZ5^7-pVNu
   13.57 zv#&Ez_m4F<mT;u`-JP1#{rT@+6*w#emiBd~>4aAQbFEfPXJQdsPCPS(HX}MRCx?=`
   13.58 zKhz+srFb7Z(gMl8ee|Bl)it!Wv6#$ww;}N^Kba>4+^346j<Pr|J<X6~t~o<S|Ep(6
   13.59 zboHyBc#fPQ2T{r(8rXwqKo9ySts%z!pohj?(?jxq!x|z=^jv>9CJP>7z9kimhj<9y
   13.60 zCFH%chbG4VR1eA1%h}N9a~<ml$<;g3e`FnzX3p1kMlC`ijwR(z>xjuP|MoO<r*dY+
   13.61 zzh>rs(4^+OvFFFlz-g9lrw^1`s^`Nq?a&T-rx`dRIi?sbY)FYnIi1k@zho+wb0jfS
   13.62 zjRrNV;zE14zh{04ZAk2>%PR!E41+rp|D)%ZUc&Js=NI(Pn_mhXBw=A$$~^FOS62(K
   13.63 z5It!G?XWFrM6><{_@|#PN8m_-J%_|&Z=uYG;<XP-N97Q$2E+p<XTiA`8AC1jE-Q8s
   13.64 zi+*&x!Yh`C#mq#rPDA$nBEoc{5vD0d1N{j9L9>Amp%5vD`XOhMj-k3_gHV@j$S2qG
   13.65 zbVG^Gz^18SGq2%Q;0vU<?>>cMgh<z^d4)*O=*TCt!Ub7oDTW4dw;qrWQz(A@Yv9UN
   13.66 znN=lbl}VdtHHZz<*kYBH<AFfB6p3NJd@aj{`pY#NP2?sYYY_>PtQKBZtP}2&DU^y4
   13.67 z89K9tCwE4K+aL<%JwVLoRc1btkZ#a)swcPaQrKdnnT;SM7sDhVTqj$3W2VET1&rre
   13.68 z1#h;piV@*!%?zVildp5KibSFzD^w^(!G+PncU%EXSXi}{*OiMb7Da+$#BB0ahf!lm
   13.69 zFJXzPDwB?f6~2?zh@H_cKq>Majya%Hq7q(;k@t{e)F@J(2j0kGc~Ci$PZ>T){X`CW
   13.70 z=%^N&3kRttR_3rO5=B5upk0g*3FICn@o%Pz#6XFn81N4CO0k+%7FCJLtP4@Hbq0&E
   13.71 z)cT-VL!1Y$E@#aSG9dJhoCSyiBaH?jB$71?VHoMHR&iL9?K+F7pfW<inX@8{nx}}A
   13.72 z0l|}OHme-Ka6Nvm$InlVpLs$E<*<elUK<Leks~QR%QDHRgT)=kDn^Pu(hm-<0brCT
   13.73 zGWa?O5nRM)*{s<>d?E~0hS9*&Xc(D51~o%vwWk<4O~^mwI*SahIz)+dyC5QTMPskP
   13.74 ztWuZKEAW2!V~~8cir7OKLLZw%kjPhUIPiJCR#z;>I8h7Y%Ayd<Ar^8y4RXOuFGQ7Q
   13.75 zbx=kU!_B%<me-n%))H-qB}Z4HBD->nsPluIhB_!iS3;>G8_8IsnPjJmn_|@$vF1>N
   13.76 zkOFBsGpnZQ3tbghtol$6ier?_=}HVFI7qhSK=@fzB~<Gf5Ot|qs}NgOs2>@oMYuww
   13.77 z%+P6!wp`Y%X31BUSVh8^aMgomlU8NuQa8nzVrEsP;Zf6ZobaVHajqok0R2r@aaPvS
   13.78 zC6`7<L?>~cv6Q`L+b*dpv?aZgZ&ryO6GTx$gjy8w!UQA1w1~v$0bQ>L_x0fZFA47J
   13.79 z0ewB7|FZ*nL?|S$$M*Hu{x6H|E&<)`I(;1YGokBJJwfc5+!rCYg+-O=rR70GsGEGP
   13.80 z(MD*YYf`2$Pe+bibqZlZ`1?E%A?imBLAOF^1d%WizD!hLBvR=wDk679Ba#igE}48l
   13.81 zNVbjBENliOo)EZn2S>k-6bjxvmLv#l6a`&E-+(2_LOwJM?Wks$b>wRz9U%_RNY&~%
   13.82 zjTttl;Z-4N5!&fNzNlG?MdT$)|0G#gH!m=n6u$x}8aZ~fa%=~*j2wN>Xkn{KH@Rf1
   13.83 zPJF0Tj16tzQnf1L0CJ<2CKs`dEs5MO{wR&|AY)l^14*{bp<OZQ_9qDnd5GYKNa~{E
   13.84 zShA@tR~gi>wI6qnQaLVhB!y2Mz)VS~CgGm(iHQk`-FYI!+ST$Ofkj!C6q1_;Vii@A
   13.85 zfJaDU>Rqyh58npqxR`8QsLQtMbwU<*B2Ct5G#uNR25m#=NHF;)IWRQub>gC--H9S#
   13.86 z=Nw`BqHpsQSIuf2lM=mA6PY>TX_eGGXx5eJ44v;E2z^c_bI}~dXr&@?ykcB}0>&zF
   13.87 z9JHr*RK&SO<e~l3bRGTgLSn%V;{7=tu@%YD>ApzBD&pEvJEkLQ6UN>%K4JXW@z<mF
   13.88 ze@WDK9qP`ZJNiE$SVxb9I7t{ay7SGuf4^tD>eb1!;P7$(2}m%8q@{B7xc_a=?W$L2
   13.89 z=Z2PjJ^FTR;{OKcjvaN+_)+7=D6avtU2jNVeK8`{Xe`x*&^K3W<c$_Btcto@eZe}I
   13.90 z?u@$3SP|&miC_3FtkJBe4V4k5kyf2+M~X5>c3Yd!(>?Ycj1%uLioYm`_;6ADBp@HQ
   13.91 ztsw{{Uyi09F<$+Ps%-i-0SlB%kridquM22~k?jwKf7)s=nmY^)NB5w6(FF7WnupwI
   13.92 zv*Jd@&lJB<C<jXi$0!-4MA=IjqwJ%UEBh(?D{oXLD^r!}%8AM>rCRA%Rx1}NS12D=
   13.93 zKBC;Hd{o(}+@gG3xlOr4`MUCR<)4&aDF3GXPI*z;s%%rXD}$K9y>JYU#eMN0{BwLO
   13.94 zz8}w{3X!NF>dYOA=Aa6+1#MFdRNSJBhE8LZeU)*_0m^tR#WEa?6?g)kga0&)<Ogda
   13.95 ztw7&9hIM?7Lgau&MDt;hS^J|sl@I4)#CP@-aY;AMAV#-}ru~3nU95=*JHU#cVgre)
   13.96 z(3BfF{#gcC6_EXR&(@7ali|D&Y|IAh^l;8cEO3nM<eq;(P{Kb;(5QcwpwSUtE6}o2
   13.97 z$nROWh?_pj2y&N?MU`DYDD5`e(x97S$S5n~Xrx^K81=vKW7K(_ZVYxC2U8&Y%=D)`
   13.98 z+^`<*B6dvpiT~(5!-R2v?@hz2Z)JAeA-#S_@!xVsvD-z`|ED{O9e1-sAK-U;bG##p
   13.99 zJ0}GV>F&}Y6>ym>Gt`h4VoIpZIujoXqmVKA4g;@cAxJ|NlUaxwwdJam31eQzZ8|VI
  13.100 zVf1(<3~#c9i~(U9Icn@U@}&cTpuA_)_yi^Fv>}3p+k!wEH+t;&@np?z;mNMMz!(nc
  13.101 zaQko=13c;k+`ebz=<%dkxT-*_Q<oZ8i-q`JsLM>9lr03VPKex)zo&Oc?dt8Y?=oRZ
  13.102 z;pq3oc$ml3NcAuC-nemzqawYRFlN;FNbijvJvO1M_ePCVDtq#t(7rqGDaVZ=8;+j5
  13.103 zH!9)p_Z|gjD@?i;>Lc+E^_av_M9+wd2yI4<5-W?kZ&TD`3s@#_;103DI647!sFGW!
  13.104 zLYg`TX}!N>0*NYA0A&u!wQx4U*#&1l8-MM~L`X)Cg2LXv9*R)EWFOKd7Co@10L9(w
  13.105 zQlooZI_VQryfTH?hvs@KnFenFNfH}TVkVMYE<^~q3Zr=PMe?L7lnHVYDG%^w<14!1
  13.106 zQc54<ZA5cDuV1hy`p|vZxLpr?anN2z+LJQT4|KUP8)KLMjKJ&pQ>)1T{j1(bncD3h
  13.107 z6b*W>cLfH;*U1oqas!-YaDD{N(WKv6=+A|yK6jIT6J;n4>SCd8y_W&q)KD*#koM3&
  13.108 ziVx96S}f?9wCPsV8}!d5I=CK}#AoBSEa0hiMN6MZ9E`mFGq}xv0AHHoMG9B6<O%R&
  13.109 zbi?4FqE5wJs`D{zb&V4AY9qsad;o3#_zc?9#Gsd&2BZB=$>{AS6*|)7LnoRV(dniG
  13.110 z=xoy&^mQ|XE;SEkP;)X9-K=8bntjZ`=0--*e1I9+e1=iF3elY)`z5amZ1$sQDDQ>i
  13.111 z4e0A_FX>aoyaVSpI3Mw<BnZw&!Jmns_Zs3~FT<Qo9L$_1`ghhD@ApdSm-kU}6;k*>
  13.112 z_Rv^bj(16=_CznnnW)byZ!-9aL7AfcNkm3sQPM#1JeQp7!6zZP6F!?*OJsw86<+e~
  13.113 zVUSaIE9k+ZXkRsnwLbS#nfsvcUL-#7$w>Lczg_jr#>+)pI_nY11O5H%T0Fk0sG*ri
  13.114 z{%UtNb>SNhd~3SptGV5g2>sMi-BD~}Az}!<7Ge_OE*EG8%X%XQ%BfI}PJ9b7wY_QG
  13.115 z^e)%sBt|1*HWz#!E5>eEF7#)QjB7GJNnR!i%7dXCor##ywVxwIWV*hH;t8KC3jD4{
  13.116 z4CyObkcFs@z(aT`&le&e@Q~ac9)$*zHe@^!y)-Xxxg~()r0>K4K0@1gZ;K~9ghu)Y
  13.117 z-qUr}P#^MvNEhX_baxGMtVt0%CBA_e$a^jE8Q~NCHc)IU_)xTkwwHk)WR%{?F44d7
  13.118 zP&d768?ml~-w$%UoqV1L^`of|1ELRQBo{D8{NUOY)|uKFmc2#v`_@ivj)wZ#r^0_F
  13.119 zapkJ~xkHStx(OPDCe*;Ojpo{s`bQC}{~eMI{W;J+MbSXpTYms0)&us{10K~iqBO|;
  13.120 zBpP!nl<7Hj!49$r)(%bdAT<7Al4FR;L{WdD@k)5`XYq+r#1KBfvP_rkY*MlpAB9*>
  13.121 z0v(f*yl5tr)5P4*f%1LOF42VsANPDNi3i!k3C-t2X`a)Ua!Ee|9z^*<eI!aDuZes(
  13.122 zhEn{;N1$w{I6oMfUZJ)Nm_zWP9x$`M5x0c$%;o;M1N^aP!W&cvmv<wI^Zo+pA^m=V
  13.123 zV#E6V0&w0<VkbgBp!qhU*v|U(K120$!<<6&`vsL57t+tOKWa>p55;-5{KcN#_U=3p
  13.124 zFJ_W!F~@U5`0fW?=@=&Cvvdrd;W6Ap#t`%>AY%sSB!3g|U%_n?Hi3@6ynNiVf$FI6
  13.125 zJbF48oGOkH)i0UoH{n&pxPEbojLGRlC%y!{_lI;LI=n*ryc(~w+uj-39c~f-iaeWK
  13.126 z5A*@s-XZgPJop9XfeU5gJd3=y!g^pZ=m_mYI7{Yem}5q}7NgNLZ(@K3*yh>+w1Wa|
  13.127 z{N2zGNEn=il3;zXXZ$e43?2rtgeJwihvnwFhe@9p4($@p;nT@p6z9_3r%XIe`yA-1
  13.128 z%~2}OLHm+Kr8|0p-BpsKgnWZFqsZ&eREl^r5Y`*Y!3yAAoTGH9v+b?`QjSl;SosCX
  13.129 z%faz3_0Qtn(aG_yFUH5adMCxX?-<{o%7~WQ-FIc%m2$+SAbcXkOLNsr@rmZ+f2+N2
  13.130 zS*qRj`2>5&Mj98}A!hf(+5s@C?sX)qyB*dJsVMf<EhxSX$yR*)Ix775bEIvOB2&}t
  13.131 z$ljESyiICU+vG=eO<U0Vrq|J?rq9v#W+~d!d^>unITh`1R-?C@{pd*Z7IdQdb#%J<
  13.132 zb9DBE6n!1(-{v#;Qu6`)b#o&=+w8-qn^pKkb22{CJQ%;-%;5b^XYfl+2k@SzM!dbr
  13.133 zhc`8;@cO1?T-P)h*ETWO`|%lU|M&nlz1oO}ctLkB=<eDseZqD48-*8a@PZA$1snbV
  13.134 z`u_p+KM49C1pN<!{s%$-gP{LG(ElLle-QLP1o|HW{SSfuhd}>Bp#LGz{}AZ^Ht2o`
  13.135 zY=Aifx%{`=+k?GU<iJ`eg!6~MmP266A+Y5T*m4MLIRv&G0$UD&EeFAtgJ8=+u;n1w
  13.136 zau94e2(}yqTMmLPe*jzl0Ji)VY$5V|RcL4(!6#T}d2_(77-4M<^tc}A_w_(8=z*Tu
  13.137 z13j?^I_iNQ+XMZU9_X?j=)-%U$M--V(gS^94|GKj^rRl>(LK<WJ<vz@K%d(KJ+}vX
  13.138 zP7m~%J<!v-qmyewC)b2dXe6%4ufhOQ?BdUT?ZP1^KYINd=(Pf!u|s*i@EcG`oEJ$N
  13.139 z5@}hs73kXqdaoQPn*{z|dj*&xuilU$%yS^K7NMw@fWB4WiJlAPlR`O$z}h3iQyyZD
  13.140 zLiw~%mY*j4Lb*=>>VRFo0j8&AKz~kXdt(yF9|H7U2;HOs`rCp&HxGxU%`PP^4?F?o
  13.141 zTA@7TZQz+Jw7p{pl)VD~9bgM1I0W5E?D7iw-$i&X2|5hDh3F}?9ZGC@M<@?_gOmk1
  13.142 zch`ZOAp(7Pffx1475W`XWQtGiOGpErp+aAyIG_gv`slM>Bo%cS9}V;)0{vd_k7QsH
  13.143 zE#H?5ZQBHWehogCoD}HEL!s?xp>5hG;7JpB(*Fo_nee;b%(KGtTjak(=)ohzKZ5+J
  13.144 zE-1ev@aKaqvR(9t%xI<w%DKWo&fO3CoE2=Dw;g2K1-c4&m>eM<)L^e9Ua+0j0{w)b
  13.145 zPYEo`m^z`XQ$pJ~fu939dQQ-RYXP1(K~IAW%66e_1l=TQLcivLpwA;h+w$k2+#=Xs
  13.146 z0s1hb1^rzofnStU`B&h%Ptem3{*e?4ZL4d6|D+&uk;WSy-`crEXMtxa(P5y#vkb;v
  13.147 zwqEc_9rP=WO`;vwgHI&l_^yS#kR2`CPs$s>=b@b1bRWo>8$<aU*L%aUz1iMT-ZB$-
  13.148 zVub;Iya8ke6kbx^PHfo}N9j9>A9&&S!%snON^_Ky{u_`lsZ~<`XJPE50Tb2X#Y`Z5
  13.149 zE%^K;l3$wyeO`iG=)2uT`G4;Po%uw{e+c|9d*>laKLU11wUa3SQSh5=kMPs%6C{u9
  13.150 zf<8?Uhmz;wDE}wKSN92heM-jkCBd#wF9F>vjK!HWpzj|_+kOW8=#PTmK08b4wEQ_4
  13.151 z$6BHMCoqZGK9tJ;^9jN)(7zzqBIe6k($@*WZ|5L3m_5;y|4WkNB5wSJ<kvF6_P>BW
  13.152 zNR0EZ3P_&Prs!`l7E+fW{~Pc_Ozmw{PAhSf_{{FMSm2Kqa=bmBY@3GB#zBH(ErLv(
  13.153 zOMFhtuwNp2Mhj&a7bc1NhmnxnypQ^jk;3@O>au7#n&|1GVwh;~3F8v@<r?twSsD*a
  13.154 zKVnydATy5idqODpAMFicDKmf!iSS$mW<a1r<_!eUv|<^~Qs`BF1Ti;(&qZu!ZU&tP
  13.155 zm>!|}4BG?!&ZNKaXNH4YrDscNc?86sBxw#UCy-ozdnqlC0pIqj^HV)1ko@}kUMlB)
  13.156 z;@h+oT29(a@+*OsQy|Wznj%_GzeMEBrsXV>Qv(ItvtitO-)E=vJn*?JEo(n1PltR#
  13.157 z8o}P_Bp!shgPB3-0_HF?NlI)L?3#JBqdbe~vsd7m2YzD`h4Osje_oKeU=#5<jR&TP
  13.158 z*do%^ZBT9y`YHx&K>{Q&C1fnV7I?Hd9ds?R_mUvL6k<T45y}RFMMG)+GA4rOLxgy+
  13.159 z5dDRDgyDxkxlSxUN5)Z*ZzXoUBgnUcKH}3xnDReDxlkzE(%>EDTlv>eeh26?1-hNc
  13.160 ztQBNB$UE$y`%0h;x}p67-5G$g@O&=Dd78}i6h9d^-w~6QUy~RX{Np9_>@7lDAHkem
  13.161 zf}TFeBlMC`uG$sGb0#o^#1F+-rbYvF@>l@m^`Qb!NweKk`?=kBJBs!BLD3kHvJAyT
  13.162 zI|hRI&VpNR4Q9#*d_(u?>yX!F|GC{OqjDK~ulQ!P3CaV=Js`3s2cAG?)X7!EDPY4{
  13.163 z4~nwFo;%>RA+J};+#=p1;Gs-qKv}z8iV9)RC4ur>I7`W19nLaxFTsB^vL}-JDVtDS
  13.164 z;(9bZGiDV_Wcy~K`#g6ZvU^HCw0pG8_VKr)q<SB0+ets5gSQbCWX5FE`;bKEp|{e0
  13.165 z<2;|Y+OvbN^(XY9zZ7Y&UjW*Hc0%(=qj=C;*_r>qnQr*sB{Z^^BKMogy=JmcfB6hP
  13.166 zdDVSeHL?$t(zdq{AA^4fX3G07r%x=1c&?{SMW6UWp5qWh%Jfq-@o@qj1h|MM7vl2>
  13.167 zXa)UbuggOX`jLuU)@(76Pdm%IR>ya#evsF)<}N&P!joqtk00#mi3gFdh$rcgbd`7a
  13.168 z%!0_L>6lrArpwcGdjG4RrW1Oo%Kp&Pa2O={v47j}kNvwY6{Pk@eNik#K{WjJrhl^T
  13.169 zrM4mIm`x|Ye;Lby-=EwxrVRy=M6&tYXR+kl&5|GjS+~Ev8%y85ojlb;^7d0$@;2Zn
  13.170 z+Po7pn_&+87S7*}L7R49v<aPr_mk**c>jJoM!|oAa#)&{J752@PgbFypPSZ(gIJQB
  13.171 zt9#Zb(d8xwF~&^Ftld88tQ3qTIC<7nKFO?P%;4OeJ|-74X*xLT(opUWAIe4Z;e9^R
  13.172 z!@GXF4^?dQb@%rZl}n$x!1%IX<~A;vnogBV$t!)<FDcDSp~|Js-tCvpP9@5v%znx*
  13.173 znVka4<?Zw{d6-Erg>z{-n!3Y}rlJM#z5p5F-MHP4+}r%!{ryDcGV%&dF9&3mroy}o
  13.174 zQ916}fP@p3OPjMhAf1y&m7DWaKr%;AE<lva;NZ+<pu8Ocl!pr8y%3q;-LyS`Dz^o?
  13.175 z`|BLZJ;!(#Z%5X2HZ`)Qb3X&oAaozPAKi<_qH$;f{KX?>+rD#%$z9osBrEqRFCl5+
  13.176 zdA$1y%)9th&<i!Y_d(4*oCG!J52H9*v#%X7`*sJ>jk~=l_Y^+A5|$JD3Qyrev=X1+
  13.177 zSJ;kjf(9YdJ|H3GK8zoWLxYp>;UoaF$FLG5;ld=m8=3G*<i&@*=g%W0R4T;AB8l+F
  13.178 zoj(<)#QP9RYR7|;P%bpseI7*Q!YcYaFeX74Py{{2mLUR_jSgcaQMnEEK7UF9gng}8
  13.179 z{)4T&7?vYI6Iw+-hWB9wxo$%-KeE~3V3Irz%mMR22XIuMxG?AW!&f&|VDKzH1s1m9
  13.180 zUP%a@!nr{-U?o1a@4P9@w-WC&wO`c~6`ntQ>bw$t13P8#>_x;Vll}s`+N7i)nI+=T
  13.181 z4QMDDf<~iX!CwLzj{3IZa|q*B6vUTMJHCP<uOYe-kb<}!ib49oq&9eKrRR2_BU)}3
  13.182 z&*TVpIY;tc4A)7CEXW=zGKhPN&uVL>tU(+VdVX7w9zom&auB-mQ<%y5n#@<I_Ouyd
  13.183 z@DK9~HeC_91v_N4x?Qv}h@-;~o+I_&K+Q$C`U`Dw-Tl|y|9{y19jn+}6))apKm*`!
  13.184 z5V{4W!ZvFb>@{bgX=n;e<5N)q{H3EL^Z?9IePQkzKxeB#D6I$1j4)?^QRX!{+pz5H
  13.185 z{@*<F@++^%UVP>^`_Iz3J^0%l?a#gPZ2NPP9ls5d`91opZM&X+`l($zcfR^0-Wk2?
  13.186 zsi&Xbwe2g6``}>Xw(Z-uJ-(%J)24lo;Y~4(TOKEb#vqpC9h({(H*MUoZta?21Fnx+
  13.187 zwQ|LZm36D@ziJ59_gP(E-_TIMdi5$Ov^7N4uU@s1)Lm(iuUWfp!$#6_2bO=jbi;;+
  13.188 z)~;T$ba8EPk-WOf<FwfvE?=PL{Gxs})&44<KTuN>@Odh|{_6HcF;H|lE8JDpHLZ)}
  13.189 zwTqXoSiSb44I7qziXIKt_o`dI7+M5<eY|d)NyoA}qt)qd_uxvI+g0Im2P=^~=;~MD
  13.190 zC^ws}j><}>)nqi6JKJ6I3R_vJR%hVtPM5pV8}#+6@>efjUKgxKk4AZxFJHD~QJ|^~
  13.191 z#5&8WjvYOE{Dj_I9&}0_wsJmbzT0ZEm-FT_v)Q<C%`2s5r^~8;<s&~=W&v6mr&-^E
  13.192 z-_;q)phuUd-4|6AShQr>^5q_63oeq>1gctn$sT)Y0H1w)$L?aC-c(j*;1-q?v&Fhn
  13.193 zeQAlNNW&J*J$S6J)aEQJK8F3O;$lsaTD@R;%O_Kc7N|AFI%7GJE2#?91Q#J2XdVdo
  13.194 ztFD05@)8d|oH2Ed>Z5m|#k)_>T5=AzJU4sZLr3e+;fDNYaLchxR=YuU6uYLaJ4R?z
  13.195 z-^Uk@WSfs-d_b?aRkryAX>P{tz35BvSXno2vbfm!2e74}60fr1H`CVR-{d}lPj0@@
  13.196 zGHD_H#A~<O_47Z(w#nNlEz^RVjJYjmXBhD@O<9HavXAk)rQitfC7;w|)wtX7y*2Zf
  13.197 z;-@FRc_IBpT=75}Zpqq$O-YAwMjHNLW{J(ApZ5XgGq+M&dMZBrKm*>LnfVE>;2hpo
  13.198 zpVaGdIb`Kdw+CeRClc{{6Q>m}n111lX?R!ad#(8H%+1*J0JH^pv$UeTj4hOwmImvr
  13.199 z$MEL#^ds1>vwPcoGOx$&ESJGpRf512PkAx;Fmsx!X!a}kJv@KX5o}7$$(sr$^BM2s
  13.200 zNt5u0b96R`VZn#EeA43~+WQ%s@z#kG-^bOZ4l1+K<*>@Ec2TBnp&y^L`xYA)STKGo
  13.201 zZ~8|3R(AdzcJ`ClmHQ#io7!?}KFBm_j$v2c4uO`3Kg`wOPqL@uFN=&7qD;HB1?+bk
  13.202 z7q{WWmBsVF#4A*q1ux)p$G_XA-h->>9>??NzKB2CRqo_VKf=}Xc2nBCdHAFGbJw?g
  13.203 zdalJ?YIR@qwfKJKb(b58RYk@6GLv3gQfe$S=nP9&SdB(QTdAB|s14MWIb06j5G+O9
  13.204 z<x<SWlxhu&7qc8+;koF$;2Y?vu$hg<GOOKgvs!Hqr=2e|m08MdwzhJ)&B|MNo2%08
  13.205 zv<1tN?Q%J`#gy}9M##L1N-t>+V_4}5I{QKDRl41k9=LKk98T92x4aVawgNH|ID&3e
  13.206 zdD)FCqk-gbRKS3L=R4zT_kIt@U*Y%`_6H);z4FRl*-Ig$iweH{<nu4Q@ci?yK8K&j
  13.207 zFG!z%@@0y5lCQQu9o#K>de^R3cj8?L%Ce`o1K!DTaP#&ZJGMWub<5^g8|9m~Y<+@U
  13.208 zZVuv`@spdkY-xP#;fL0(UAtyYL&NGi0JK&04POEH^#QB{T7CU$D73AKYN%gTN9tPF
  13.209 z;I%R99(wq(#w}YmKZ)h1mOb*whII{f%a<+*E|xC}cwJy>rN5^3{NjGKi>d?u>YCcx
  13.210 z8o$?DRlTTvaSRkIU2accQElsD`I4o}>l)T=c;t~~r_f`;HN94^Tna6M{ytS6hk0Rf
  13.211 zNokplKmqp3JeA<bpci?9mHpfmcAmFYc)c#0xs12F+AHO5hlSIXn#!H7N{_cH=<gM%
  13.212 zUbJ-O>fjpmSd@3=$`#8N*96)?tjpp*e)QP!kBz)N=#o}A?BzlJZZf{*Fs6K2>Ds*<
  13.213 z@2a#JUO5pkSjaeA4D3TK_`OmS40%PRuiYONs9C&h#mbdl<OnX7)Yb%A{mDKjSB=j;
  13.214 z@#Jo8sljZqm<*h*q_||E-oR;D4O^_9ckoz|-r=&89LIs85<+8DGg>~$Eh^Ly8|_4{
  13.215 zBv4ZuT#Ou`c}-1q;0j2!Yhm*=F>h|sNAGD^&AU&}UJ7vc{G9m@9cws;*G!{uXLFj0
  13.216 zj$-$W^@P@t_W{7&WD9{igAp>-FG%w+o~n!f6rZiQ5;yVg;spmVKh29*1Kg#r$Ina^
  13.217 zpe={Mozvzdxo?}Yozk*++-#cKa&9KTU9rVob=l8Ud8FWos!M(;$RlvKsBjs6I`hp7
  13.218 z831=l>9}RmR$P{R1W!x{c+xp4h&XH3<CK;`;Asusot5<ocIzvuTK&>0UuA{N?(+CR
  13.219 zc7Gxf&}q5~-1EhByesW6z#YJ68G%obH(Qj)Puxmr>FM}L@+Q0`17Ipp>a1$>%c^`H
  13.220 zmt6*9<pqH$f)BH%t2J|8!H4mJ>?61=ZE}7-m}~_&%gM$c&RyuJFsi`h>?bHKI~%`0
  13.221 zaSPs>nfX4h;VP(1Z)Jr|W^;-%9Wc3_bXF}bQvuxlzF@|q_^q62bBpKf#O|pd;)48^
  13.222 zQwvHR6=wDrcIQ7yY5Dp1XkIb?G-n3>Qd8y@WjbvwV85$uDZpKwwh-V>&8l9&UmpK%
  13.223 zyXH5zX5MkUVBU-P#8Y-xId=lr%-=(4^XKD_7R-C7<<z+r4`=gS^tbp2Re9{j617Ha
  13.224 zu$T=x9Zb}wg{EaIZDnOfeUQWYzMQUlm8GJxqTI-FdV{_lXtA6Dre)KTB_)P(x9_6=
  13.225 zf`6dT?cmGGEH<ap0V|dYm$Terwpi^BN1I&^D+6n}qmoQcK|69>wqr+(z1&g;`RDdl
  13.226 zk>)Ux-bz@EKmvL_9<L9s0P0+oS3Gh6J2${v6>tPS$a~p?z0p7dAcSH6&VR<=UiCd3
  13.227 ze}&^)|2BUcmY&`J%rh_VmHqaaXF{vHmv^?m@XGV;FGzO2EUxahKfPzq?x&x6>eXHN
  13.228 zspzM7@7Y7q4pw)YcRcyz4p`o8-n_37Z;pY*9U+AAZu4W0Y*@dJA|8db)eURDS{q!`
  13.229 z2ha{^u)u>t+uEo#4Xam?y4JOLUCjCok36<n#JlAiH$J?+VbzLdOM^?~wbeei(^&y{
  13.230 zw)p&#ev4}%b!!$aUc4ya^8p05FNuMo2k^eSc5&+x`O;-8RyC}Dc;m+9r(!m(S-Ymb
  13.231 zZW*))2KxBDPD^QtmMa5qwpU>?Yl9zyJ_HWyM<!c<IiJsMx0LY?SpUf@oxFY_S7sx#
  13.232 zhR+`i!~n=HtE&f*o1(mRbt{)ISybHyV%?Si;N3?bo2-tYdnC+4FnL++&I*T(;vKhc
  13.233 zuiom0;Msd3VB`q~T8zaHeTUzJ5Q5p=Q`H`bs$R5Y`O3OFFLDN#NER=uZVmLQs?gUE
  13.234 zyxXJWj6q8RMA<@Z31FX5uPX*b)673~tjOSW^V;LMTCD{d;N48XJGF`})^cVCkt?ZQ
  13.235 zv^cl~IeS&tELv1keI<}w<<LQ{X6DaRp8&jL-`hQh;@#W@8wj?oo%Sr?U8B9COil1k
  13.236 zz`%R~?~Y;on$hI+wFLxeUdHRc7)YtImr%SbDLjC!(*f`7_{|K!yF3x^O7SPY3VVgI
  13.237 z@I!2WP{6xMfOlnpce70Rc(J9@e>uSTy;5+5|589&WiPG-ysM>n_g2fq7qN@rUABmK
  13.238 z8Gug<oi3xebPC~J@*2En(j>q;gUjC<kov1UE|~*z24wdq62asdMc|$<X5gpN3EoZG
  13.239 z5|%edlt-3}R9*=0$Wn}}@V5nI{wlBAK>-v5rU*WqG()YKyBF}TFz0<-mOiB*A51O>
  13.240 zJj=<!AI>Xvx=gS{w&gq#qP?HF1wWpZ^#QKYyQoZxq4o+<CYjw%I{komJi)t}fOnIp
  13.241 z%`2G;c$asS;$0!<bXj0A?=E;!pcM#sH+d%Dow-t!Sz&Jh``r}pR_OrmR;r6tFH*c?
  13.242 z0q^F2gbU{rymPp1dceB{dnj$e0{l_oe89Uezw_wr-iv{jz#zZZVbZEKI-|v6T)0qg
  13.243 zhM61i&R$k#G6eP5&{w~(W;O5fxNIi9-e5Ge11(l>H0nzM@3cl+W!1&Ng}}fn*e{vQ
  13.244 z7JEg7(@yqD6*j=Ta);B|=8!w>wsM=(<MX<mK?ibPc3@|W!)7Tn^NvcNpEQS&^m&5r
  13.245 zeiZ7wJ^(k5+XX9|D_*$|vbPfQ5;$PV;=Am{zG$)}gynLT|GU7MK)e5YIQ|O9w}EYe
  13.246 F{|9wAU{L@7
  13.247 
    14.1 new file mode 100644
    14.2 --- /dev/null
    14.3 +++ b/permissions.py
    14.4 @@ -0,0 +1,41 @@
    14.5 +##############################################################################
    14.6 +#
    14.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
    14.8 +#
    14.9 +# This software is subject to the provisions of the Zope Public License,
   14.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   14.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   14.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   14.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   14.14 +# FOR A PARTICULAR PURPOSE.
   14.15 +#
   14.16 +##############################################################################
   14.17 +""" CMFCalendar product permissions
   14.18 +
   14.19 +$Id: permissions.py 36457 2004-08-12 15:07:44Z jens $
   14.20 +"""
   14.21 +from AccessControl import ModuleSecurityInfo
   14.22 +
   14.23 +from Products.CMFCore.permissions import setDefaultRoles
   14.24 +
   14.25 +security = ModuleSecurityInfo('Products.CMFCalendar.permissions')
   14.26 +
   14.27 +security.declarePublic('AddEvents')
   14.28 +AddEvents = 'Add portal events'
   14.29 +setDefaultRoles(AddEvents, ('Manager', 'Owner', 'Member'))
   14.30 +
   14.31 +security.declarePublic('ChangeEvents')
   14.32 +ChangeEvents = 'Change portal events'
   14.33 +setDefaultRoles(ChangeEvents, ('Manager', 'Owner',))
   14.34 +
   14.35 +security.declarePublic('AddPortalContent')
   14.36 +from Products.CMFCore.permissions import AddPortalContent
   14.37 +
   14.38 +security.declarePublic('ManagePortal')
   14.39 +from Products.CMFCore.permissions import ManagePortal
   14.40 +
   14.41 +security.declarePublic('View')
   14.42 +from Products.CMFCore.permissions import View
   14.43 +
   14.44 +security.declarePublic('ModifyPortalContent')
   14.45 +from Products.CMFCore.permissions import ModifyPortalContent
    15.1 new file mode 100644
    15.2 --- /dev/null
    15.3 +++ b/profiles/default/catalog.xml
    15.4 @@ -0,0 +1,11 @@
    15.5 +<?xml version="1.0"?>
    15.6 +<object name="portal_catalog" meta_type="CMF Catalog">
    15.7 + <index name="end" meta_type="DateIndex">
    15.8 +  <property name="index_naive_time_as_local">True</property>
    15.9 + </index>
   15.10 + <index name="start" meta_type="DateIndex">
   15.11 +  <property name="index_naive_time_as_local">True</property>
   15.12 + </index>
   15.13 + <column value="end"/>
   15.14 + <column value="start"/>
   15.15 +</object>
    16.1 new file mode 100644
    16.2 --- /dev/null
    16.3 +++ b/profiles/default/import_steps.xml
    16.4 @@ -0,0 +1,9 @@
    16.5 +<?xml version="1.0"?>
    16.6 +<import-steps>
    16.7 + <import-step id="various-calendar" version="20050316-01"
    16.8 +              handler="Products.CMFCalendar.setuphandlers.importVarious"
    16.9 +              title="Various Calendar Settings">
   16.10 +  <dependency step="toolset"/>
   16.11 +  Import various settings for CMF Calendar.
   16.12 + </import-step>
   16.13 +</import-steps>
    17.1 new file mode 100644
    17.2 --- /dev/null
    17.3 +++ b/profiles/default/skins.xml
    17.4 @@ -0,0 +1,8 @@
    17.5 +<?xml version="1.0"?>
    17.6 +<object name="portal_skins" meta_type="CMF Skins Tool">
    17.7 + <object name="zpt_calendar" meta_type="Filesystem Directory View"
    17.8 +    directory="CMFCalendar/skins/zpt_calendar"/>
    17.9 + <skin-path name="*">
   17.10 +  <layer name="zpt_calendar" insert-before="zpt_content"/>
   17.11 + </skin-path>
   17.12 +</object>
    18.1 new file mode 100644
    18.2 --- /dev/null
    18.3 +++ b/profiles/default/toolset.xml
    18.4 @@ -0,0 +1,5 @@
    18.5 +<?xml version="1.0"?>
    18.6 +<tool-setup>
    18.7 + <required tool_id="portal_calendar"
    18.8 +           class="Products.CMFCalendar.CalendarTool.CalendarTool"/>
    18.9 +</tool-setup>
    19.1 new file mode 100644
    19.2 --- /dev/null
    19.3 +++ b/profiles/default/types.xml
    19.4 @@ -0,0 +1,4 @@
    19.5 +<?xml version="1.0"?>
    19.6 +<object name="portal_types" meta_type="CMF Types Tool">
    19.7 + <object name="Event" meta_type="Factory-based Type Information"/>
    19.8 +</object>
    20.1 new file mode 100644
    20.2 --- /dev/null
    20.3 +++ b/profiles/default/types/Event.xml
    20.4 @@ -0,0 +1,26 @@
    20.5 +<?xml version="1.0"?>
    20.6 +<object name="Event" meta_type="Factory-based Type Information"
    20.7 +   xmlns:i18n="http://xml.zope.org/namespaces/i18n">
    20.8 + <property name="title">Event</property>
    20.9 + <property
   20.10 +    name="description">Events are objects for use in Calendar topical queries on the catalog.</property>
   20.11 + <property name="content_icon">event_icon.gif</property>
   20.12 + <property name="content_meta_type">CMF Event</property>
   20.13 + <property name="product">CMFCalendar</property>
   20.14 + <property name="factory">addEvent</property>
   20.15 + <property name="immediate_view">event_edit_form</property>
   20.16 + <property name="global_allow">True</property>
   20.17 + <property name="filter_content_types">True</property>
   20.18 + <property name="allowed_content_types"/>
   20.19 + <property name="allow_discussion">False</property>
   20.20 + <alias from="(Default)" to="event_view"/>
   20.21 + <alias from="view" to="event_view"/>
   20.22 + <action title="View" action_id="view" category="object" condition_expr=""
   20.23 +    url_expr="string:${object_url}/event_view" visible="True">
   20.24 +  <permission value="View"/>
   20.25 + </action>
   20.26 + <action title="Edit" action_id="edit" category="object" condition_expr=""
   20.27 +    url_expr="string:${object_url}/event_edit_form" visible="True">
   20.28 +  <permission value="Change portal events"/>
   20.29 + </action>
   20.30 +</object>
    21.1 new file mode 100644
    21.2 --- /dev/null
    21.3 +++ b/setuphandlers.py
    21.4 @@ -0,0 +1,47 @@
    21.5 +##############################################################################
    21.6 +#
    21.7 +# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
    21.8 +#
    21.9 +# This software is subject to the provisions of the Zope Public License,
   21.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   21.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   21.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   21.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   21.14 +# FOR A PARTICULAR PURPOSE.
   21.15 +#
   21.16 +##############################################################################
   21.17 +""" CMFCalendar setup handlers.
   21.18 +
   21.19 +$Id: setuphandlers.py 40391 2005-11-28 16:21:21Z yuppie $
   21.20 +"""
   21.21 +
   21.22 +from Products.CMFCore.utils import getToolByName
   21.23 +
   21.24 +from exceptions import MetadataError
   21.25 +
   21.26 +
   21.27 +def importVarious(context):
   21.28 +    """ Import various settings for CMF Calendar.
   21.29 +
   21.30 +    This provisional handler will be removed again as soon as full handlers
   21.31 +    are implemented for these steps.
   21.32 +    """
   21.33 +    site = context.getSite()
   21.34 +    mdtool = getToolByName(site, 'portal_metadata')
   21.35 +
   21.36 +    # Set up a MetadataTool element policy for events
   21.37 +    try:
   21.38 +        mdtool.addElementPolicy(
   21.39 +            element='Subject',
   21.40 +            content_type='Event',
   21.41 +            is_required=0,
   21.42 +            supply_default=0,
   21.43 +            default_value='',
   21.44 +            enforce_vocabulary=0,
   21.45 +            allowed_vocabulary=('Appointment', 'Convention', 'Meeting',
   21.46 +                                'Social Event', 'Work'),
   21.47 +            REQUEST=None)
   21.48 +    except MetadataError:
   21.49 +        pass
   21.50 +
   21.51 +    return 'Various settings for CMF Calendar imported.'
    22.1 new file mode 100644
    22.2 index 0000000000000000000000000000000000000000..3c3abe6c553554d8e822f9dc2af0a6984f9412ac
    22.3 GIT binary patch
    22.4 literal 926
    22.5 zc$~dc&uh<d7=MvuE1Fsn4s&6&xjgxmT56fOOd_XUXdcw2cC&VwIL)u3<cWibcG&oq
    22.6 z+743YBs~|$i5vM7aC*F6??1x(_Vhf@>v{e7d`_MkIdb$uFM9DCD7~zTDzCCCsLHI2
    22.7 zO0Tp^s2N!i6<%Q#(5z>9lzX|AL#vi$QRZb<1|k)*5Ctz-0Rk2@vAE1>=#E}wdZuMU
    22.8 zXIVvLc!p&_EZFo&_jF5#NO9I8&C@Im<jB=TBs^gWh_W=0G+B}sq(GMlO)M@$f>iT)
    22.9 zghfF5c$SrWxP?P<$%0MuFbf0KlN4tI9<TsphRotN?Vx_h8%ZJ<Ns}dHl@1e|(8PkA
   22.10 zQ=2uPxkE-$AJ4Kfa|3;)T(V%(VGhV<r4pKz(U9x%F0?8;pb(@mge1~XCgKqS7Bm!*
   22.11 zrbBo1z{F@I_3<n#gFX(l_|XDJix4d&v>4EgMza#lEHpdN1fxkpBaKE2?JycN+A><3
   22.12 z_4<+96R-cApSrcU@^Nl%jvpod%iH~T!#dzQ22|*uIKF8X@49x4cAl+X`?kDqe4*#b
   22.13 z@MQO!tv@#6!}9#L!HbLi(|s$GeZ%v;YiIgS-0e3XnCZM+eYpSd^NFsrdk#%~?My%Y
   22.14 z)VX~6(Se)Yn?Elte0ec?_1^f72e0z6?HyXWZokIv5A7Vf(|_>iIgGuZzcjP7xc2tS
   22.15 LjV*)QdvNR@u}6m)
   22.16 
    23.1 new file mode 100644
    23.2 --- /dev/null
    23.3 +++ b/skins/calendar/event_edit.py
    23.4 @@ -0,0 +1,35 @@
    23.5 +## Script (Python) "event_edit"
    23.6 +##bind container=container
    23.7 +##bind context=context
    23.8 +##bind namespace=
    23.9 +##bind script=script
   23.10 +##bind subpath=traverse_subpath
   23.11 +##parameters=REQUEST, RESPONSE, title=None, description=None, event_type=None, effectiveDay=None, effectiveMo=None, effectiveYear=None, expirationDay=None, expirationMo=None, expirationYear=None, start_time=None, startAMPM=None, stop_time=None, stopAMPM=None, location=None, contact_name=None, contact_email=None, contact_phone=None, event_url=None 
   23.12 +##title=
   23.13 +##
   23.14 +
   23.15 +try:
   23.16 +    context.edit(title=title
   23.17 +             , description=description
   23.18 +             , eventType=event_type
   23.19 +             , effectiveDay=effectiveDay
   23.20 +             , effectiveMo=effectiveMo
   23.21 +             , effectiveYear=effectiveYear
   23.22 +             , expirationDay=expirationDay
   23.23 +             , expirationMo=expirationMo
   23.24 +             , expirationYear=expirationYear
   23.25 +             , start_time=start_time
   23.26 +             , startAMPM=startAMPM
   23.27 +             , stopAMPM=stopAMPM
   23.28 +             , stop_time=stop_time
   23.29 +             , location=location
   23.30 +             , contact_name=contact_name
   23.31 +             , contact_email=contact_email
   23.32 +             , contact_phone=contact_phone
   23.33 +             , event_url=event_url
   23.34 +             )
   23.35 +except:
   23.36 +    RESPONSE.redirect('%s/event_edit_form' % context.absolute_url()
   23.37 +                  + '?portal_status_message=Oops!' )
   23.38 +else:
   23.39 +    RESPONSE.redirect('%s/event_view' % context.absolute_url())
    24.1 new file mode 100644
    24.2 --- /dev/null
    24.3 +++ b/skins/calendar/event_edit_form.dtml
    24.4 @@ -0,0 +1,278 @@
    24.5 +<dtml-var standard_html_header>
    24.6 +
    24.7 +<STYLE>
    24.8 +
    24.9 +<!--
   24.10 +
   24.11 +body,td,th { font-family:arial,helvetica; font-size:10pt } 
   24.12 +
   24.13 +BIG {FONT-FAMILY: Arial,Helvetica; font-size: 17px;font-weight:bold}
   24.14 +
   24.15 +A {FONT-FAMILY: Arial,Helvetica; font-size: 13.5px;}
   24.16 +
   24.17 +SMALL {FONT-FAMILY: Arial,Helvetica; font-size: 2.75mm;}
   24.18 +
   24.19 + .bttn 
   24.20 +
   24.21 + { 
   24.22 +
   24.23 +   BACKGROUND-COLOR: #cccccc; 
   24.24 +
   24.25 +   BORDER-BOTTOM: #9999cc 2px outset; 
   24.26 +
   24.27 +   BORDER-LEFT: #9999cc 2px outset; 
   24.28 +
   24.29 +   BORDER-RIGHT: #9999cc 2px outset; 
   24.30 +
   24.31 +   BORDER-TOP: #9999cc 2px outset 
   24.32 +
   24.33 + } 
   24.34 +
   24.35 +   -->
   24.36 +
   24.37 +</STYLE>
   24.38 +
   24.39 +<style type="text/css" >
   24.40 +
   24.41 +<!--
   24.42 +   td.EventBackground  { background: #DDDDDD }
   24.43 +   
   24.44 +   td.EventHorizon  { background: #818384 }
   24.45 +
   24.46 +   a.calendarlight:link { color: #FFFFFF; text-decoration: none }
   24.47 +
   24.48 +   a.calendarlight:visited { color: #FFFFFF; text-decoration: none }
   24.49 +
   24.50 +   a.calendarlight:active { color: #FFFFFF; text-decoration: none }
   24.51 +
   24.52 +   a.calendarlight:hover { color: #dddddd; text-decoration: none }
   24.53 +
   24.54 +   a.calendardark:link { color: #FFFFFF; text-decoration: none }
   24.55 +
   24.56 +   a.calendardark:visited { color: #FFFFFF; text-decoration: none }
   24.57 +
   24.58 +   a.calendardark:active { color: #FFFFFF; text-decoration: none }
   24.59 +
   24.60 +   a.calendardark:hover { color: #dddddd; text-decoration: none }
   24.61 +
   24.62 +   #nav a:link { color: blue; text-decoration: none } 
   24.63 +
   24.64 +   #nav a:visited { color: blue; text-decoration: none } 
   24.65 +
   24.66 +   #nav a:active { color: blue; text-decoration: none } 
   24.67 +
   24.68 +   #nav a:hover { color: #000000; text-decoration: none } 
   24.69 +
   24.70 +   #nav TD {PADDING-LEFT: 2px;} 
   24.71 +
   24.72 +-->
   24.73 +
   24.74 +</style>
   24.75 +
   24.76 +<FORM METHOD=POST ACTION="&dtml-absolute_url;/event_edit">
   24.77 +<center>
   24.78 +<table border=0 bordercolor=bronze width=100% height=100% >
   24.79 +<tr><td align=center valign=top >
   24.80 +
   24.81 +<table border=0 bordercolor=grey width=100% height=100% cellspacing=0 cellpadding=0 >
   24.82 +
   24.83 +<tr height=2% ><td colspan=5 >
   24.84 +<table border=0 cellpadding=0 cellspacing=0 >
   24.85 +<tr><td><image src="event_info_tab.gif" height=21 width=90 border=0></td><td>
   24.86 +<img src="space.gif" width=5 height=1></td>
   24.87 +<td></td>
   24.88 +<td><img src="space.gif" width=5 height=1></td>
   24.89 +<td></td>
   24.90 +<td><img src="space.gif" width=5 height=1></td>
   24.91 +<td></td>
   24.92 +<td><img src="space.gif" width=5 height=1></td>
   24.93 +</tr>
   24.94 +</table>
   24.95 +</td></tr>
   24.96 +
   24.97 +<tr height=2% ><td Class="EventHorizon" width=15% colspan=4 >&nbsp;</td>
   24.98 +<td width=85% Class="EventHorizon" align=right nowrap >
   24.99 +<table border=0 cellpadding=6 cellspacing=0>
  24.100 +<tr><td align="Right" nowrap>
  24.101 +<nobr>
  24.102 +<INPUT TYPE="submit" VALUE="Save" style="font-size:9pt" >&nbsp;
  24.103 +<INPUT TYPE="reset" VALUE="Reset" style="font-size:9pt" >&nbsp;
  24.104 +</nobr>
  24.105 +</td></tr>
  24.106 +</table>
  24.107 +</td></tr>
  24.108 +
  24.109 +<tr height=1>
  24.110 +<td colspan=5 bgcolor=#FFFFFF ><img src="space.gif" width=10 height=1 border=0></td>
  24.111 +</tr>
  24.112 +<tr height=96% ><td colspan=5 Class="EventBackground" valign=top>
  24.113 +<table border=0 cellpadding=5 width=100% cellspacing=3>
  24.114 +<tr><td Class="EventBackground">
  24.115 +
  24.116 +<table border=0 width=100% >
  24.117 +<TR>
  24.118 +<th align=right NOWRAP>Event Name</th>
  24.119 +<TD NOWRAP><INPUT TYPE=text NAME="title" MAXLENGTH=100 size=35 VALUE="&dtml-Title;"></TD>
  24.120 +<th align=right NOWRAP>Contact Name</th>
  24.121 +<TD NOWRAP><INPUT TYPE=text NAME="contact_name" MAXLENGTH=100 size=35 VALUE="&dtml-contact_name;"></TD>
  24.122 +</TR>
  24.123 +<TR>
  24.124 +<th align=right>Location</th>
  24.125 +<TD NOWRAP><INPUT TYPE=text NAME="location" MAXLENGTH=100 size=35 VALUE="&dtml-location;" ></TD>
  24.126 +<th align=right NOWRAP>Contact Email</th>
  24.127 +<TD NOWRAP><INPUT TYPE=text NAME="contact_email" MAXLENGTH=100 size=35 VALUE="&dtml-contact_email;"></TD>
  24.128 +</TR>
  24.129 +<TR>
  24.130 +<th align=right>Event type</th>
  24.131 +<TD VALIGN=top>
  24.132 +<dtml-let contentSubject=Subject
  24.133 +             allowedSubjects="portal_metadata.listAllowedSubjects( this() )">
  24.134 +   <select name="event_type:list" multiple>
  24.135 +     <dtml-in allowedSubjects>
  24.136 +     <dtml-let item=sequence-item
  24.137 +               sel="item in contentSubject and 'selected' or ''"
  24.138 +     >
  24.139 +      <option value="&dtml-sequence-item;"
  24.140 +              &dtml-sel;> &dtml-sequence-item; </option>
  24.141 +     </dtml-let>
  24.142 +     </dtml-in>
  24.143 +    </select>
  24.144 +   </dtml-let>  
  24.145 +</TD>
  24.146 +<th align=right NOWRAP>Contact Phone</th>
  24.147 +<TD NOWRAP><INPUT TYPE=text NAME="contact_phone" MAXLENGTH=100 size=35 VALUE="&dtml-contact_phone;"></TD>
  24.148 +</TR>
  24.149 +<tr>
  24.150 +<th align="right">Event URL</th>
  24.151 +<td colspan="3"><input type="text" name="event_url" size="55" MAXLENGTH="100" value="&dtml-event_url;"></td>
  24.152 +</tr>
  24.153 +
  24.154 +<tr><td colspan=4 >
  24.155 +<table border=0 width=100% cellpadding=2 cellspacing=3 >
  24.156 +<tr>
  24.157 +<td colspan="4" >
  24.158 +<hr></font></td>
  24.159 +</tr>
  24.160 +<tr><th valign="middle" align=right>Start Date</th>
  24.161 +<dtml-with getStartStrings mapping>
  24.162 +   <td> 
  24.163 +		<select name="effectiveYear">
  24.164 +		<dtml-in buildYears>
  24.165 +                        <option value="&dtml-sequence-item;"
  24.166 +                         <dtml-if expr="_['sequence-item'] == year">Selected</dtml-if>>
  24.167 +                        <dtml-var sequence-item></option>
  24.168 +                    </dtml-in>
  24.169 +		</select>
  24.170 +
  24.171 +		<select name="effectiveMo">
  24.172 +		<dtml-in buildMonths>
  24.173 +                        <option value="&dtml-sequence-item;"
  24.174 +                         <dtml-if expr="_['sequence-item'] == month">Selected</dtml-if>>
  24.175 +                        <dtml-var sequence-item></option>
  24.176 +                    </dtml-in>
  24.177 +		</select>
  24.178 +        
  24.179 +		<select name="effectiveDay">
  24.180 +                    <dtml-in buildDays>
  24.181 +                        <option value="&dtml-sequence-item;"
  24.182 +                         <dtml-if expr="_['sequence-item'] == day">Selected</dtml-if>>
  24.183 +                        <dtml-var sequence-item></option>
  24.184 +                    </dtml-in>
  24.185 +               </select>
  24.186 +              </td>
  24.187 +   </dtml-with>
  24.188 +
  24.189 +   <th valign="middle" align="right"> Stop Date </th>
  24.190 +   <dtml-with getEndStrings mapping>
  24.191 +   <td> 
  24.192 +        <select name="expirationYear">
  24.193 +		<dtml-in buildYears>
  24.194 +                        <option value="&dtml-sequence-item;"
  24.195 +                         <dtml-if expr="_['sequence-item'] == year">Selected</dtml-if>>
  24.196 +                        <dtml-var sequence-item></option>
  24.197 +                    </dtml-in>
  24.198 +		</select>
  24.199 +
  24.200 +		<select name="expirationMo">
  24.201 +		<dtml-in buildMonths>
  24.202 +                        <option value="&dtml-sequence-item;"
  24.203 +                         <dtml-if expr="_['sequence-item'] == month">Selected</dtml-if>>
  24.204 +                        <dtml-var sequence-item></option>
  24.205 +                    </dtml-in>
  24.206 +		</select>
  24.207 +        
  24.208 +		<select name="expirationDay">
  24.209 +                    <dtml-in buildDays>
  24.210 +                        <option value="&dtml-sequence-item;"
  24.211 +                         <dtml-if expr="_['sequence-item'] == day">Selected</dtml-if>>
  24.212 +                        <dtml-var sequence-item></option>
  24.213 +                    </dtml-in>
  24.214 +               </select>
  24.215 +              </td>
  24.216 +
  24.217 +   </td>
  24.218 +   </dtml-with>
  24.219 +</td>
  24.220 +</tr>
  24.221 +
  24.222 +<TR>
  24.223 +<th valign="middle" align="right">Start Time</th>
  24.224 +<TD><select name="start_time" >
  24.225 +<dtml-let stTimeString="_.string.split(getStartTimeString())"
  24.226 +            amSel="(_.len(stTimeString) == 2 and stTimeString[1] == 'am') and 'CHECKED' or ''"
  24.227 +            pmSel="(_.len(stTimeString) == 2 and stTimeString[1] == 'pm') and 'CHECKED' or ''"
  24.228 +        >
  24.229 +        <dtml-in buildTimes>
  24.230 +        <option value="&dtml-sequence-item;"
  24.231 +          <dtml-if expr="_['sequence-item'] == stTimeString[0]">
  24.232 +             selected="selected"
  24.233 +          </dtml-if>> <dtml-var sequence-item> </option>
  24.234 +        </dtml-in>
  24.235 +       </select>
  24.236 +       <input type="radio" name="startAMPM" value="am" &dtml-amSel;> am
  24.237 +       <input type="radio" name="startAMPM" value="pm" &dtml-pmSel;> pm
  24.238 +       <input type="hidden" name="startAMPM:default" value="pm">
  24.239 +      </dtml-let>
  24.240 +</TD>
  24.241 +<th valign="middle" align="right">Stop Time</th>
  24.242 +   <td> <select name="stop_time">
  24.243 +        <dtml-let stTimeString="_.string.split(getStopTimeString())"
  24.244 +            amSel="(_.len(stTimeString) == 2 and stTimeString[1] == 'am') and 'CHECKED' or ''"
  24.245 +            pmSel="(_.len(stTimeString) == 2 and stTimeString[1] == 'pm') and 'CHECKED' or ''"
  24.246 +        >
  24.247 +        <dtml-in buildTimes>
  24.248 +        <option value="&dtml-sequence-item;"
  24.249 +          <dtml-if expr="_['sequence-item'] == stTimeString[0]">
  24.250 +             selected="selected"
  24.251 +          </dtml-if>> <dtml-var sequence-item> </option>
  24.252 +
  24.253 +        </dtml-in>
  24.254 +
  24.255 +       </select>
  24.256 +       <input type="radio" name="stopAMPM" value="am" &dtml-amSel;> am
  24.257 +       <input type="radio" name="stopAMPM" value="pm" &dtml-pmSel;> pm
  24.258 +       <input type="hidden" name="stopAMPM:default" value="pm">
  24.259 +   </dtml-let>
  24.260 +   </td>
  24.261 +</tr>
  24.262 +</table>
  24.263 +</td></tr>
  24.264 +
  24.265 +<tr><td colspan=4 ><hr></td></tr>
  24.266 +<tr>
  24.267 +<th align=right valign=top >Description</th>
  24.268 +<TD colspan="3" VALIGN=top NOWRAP>
  24.269 +<TEXTAREA NAME="description" WRAP=virtual ROWS=6 COLS=55>&dtml-Description;</TEXTAREA>
  24.270 +</td></tr>
  24.271 +
  24.272 +</table>
  24.273 +</td></tr>
  24.274 +
  24.275 +</table>
  24.276 +</td></tr>
  24.277 +</table>
  24.278 +</td></tr>
  24.279 +</table>
  24.280 +	</center>
  24.281 +</FORM>
  24.282 +<dtml-var standard_html_footer>
    25.1 new file mode 100644
    25.2 index 0000000000000000000000000000000000000000..3c3abe6c553554d8e822f9dc2af0a6984f9412ac
    25.3 GIT binary patch
    25.4 literal 926
    25.5 zc$~dc&uh<d7=MvuE1Fsn4s&6&xjgxmT56fOOd_XUXdcw2cC&VwIL)u3<cWibcG&oq
    25.6 z+743YBs~|$i5vM7aC*F6??1x(_Vhf@>v{e7d`_MkIdb$uFM9DCD7~zTDzCCCsLHI2
    25.7 zO0Tp^s2N!i6<%Q#(5z>9lzX|AL#vi$QRZb<1|k)*5Ctz-0Rk2@vAE1>=#E}wdZuMU
    25.8 zXIVvLc!p&_EZFo&_jF5#NO9I8&C@Im<jB=TBs^gWh_W=0G+B}sq(GMlO)M@$f>iT)
    25.9 zghfF5c$SrWxP?P<$%0MuFbf0KlN4tI9<TsphRotN?Vx_h8%ZJ<Ns}dHl@1e|(8PkA
   25.10 zQ=2uPxkE-$AJ4Kfa|3;)T(V%(VGhV<r4pKz(U9x%F0?8;pb(@mge1~XCgKqS7Bm!*
   25.11 zrbBo1z{F@I_3<n#gFX(l_|XDJix4d&v>4EgMza#lEHpdN1fxkpBaKE2?JycN+A><3
   25.12 z_4<+96R-cApSrcU@^Nl%jvpod%iH~T!#dzQ22|*uIKF8X@49x4cAl+X`?kDqe4*#b
   25.13 z@MQO!tv@#6!}9#L!HbLi(|s$GeZ%v;YiIgS-0e3XnCZM+eYpSd^NFsrdk#%~?My%Y
   25.14 z)VX~6(Se)Yn?Elte0ec?_1^f72e0z6?HyXWZokIv5A7Vf(|_>iIgGuZzcjP7xc2tS
   25.15 LjV*)QdvNR@u}6m)
   25.16 
    26.1 new file mode 100644
    26.2 index 0000000000000000000000000000000000000000..455b93477cca8829e88b5b62b1ce3cef3108c70c
    26.3 GIT binary patch
    26.4 literal 922
    26.5 zc$@*617-Y2Nk%w1VOjtc0K@<Q)6~@d{{E?|t7d0tfrEsUm6ldlSbBVYM@UF=b9A$`
    26.6 zwY<H(prN9Qi;RDOfp>X&hlz@Pe}Lxa=XG{>Z*Xy8V`GhvkdTp*gocM}ZEjs&U#+gM
    26.7 zKtVy9oSjfnQn$CbA^8LV00000EC2ui09pVQ000I5U?7Upq9{wMu59bRa4fUmxkzI!
    26.8 ze!}Vhz@TtQEE<mr!ABR62sTitU`nl8uh^`1%e_XWxDmt3D4Wmdw0g~MyVvBA0Nv>n
    26.9 z?)bvxVzKP=fPsR8co&3vdxnQ~XaY?Qb#?_74Gj>N5Eujw2M?YR1Q?j3q@|Y_nxUeZ
   26.10 zn+~d^nh^&FpsS^o7I%>i5)6fus06~o5f2F&0|OZp6SNM`&=IoI(bBS=2?@>52NMYt
   26.11 z2@kZ^*vQKh;?%>!s11I23=Vyitn0!D2?PN9`{3fUogR40hR)N#Y!osSSdbwBtqB<x
   26.12 z03gut;IRQ6L;zSu(3w3^?)o@*Fe;V3kr^=mG?}tNSwn;wkR@{&Gg$?j8unGlATGfG
   26.13 z009Wxv{|93fdBwJRM0SV*@FnK*4cvaKvgh%!bT2ofE0lTraFrP*kFK{2n8B6bO2BQ
   26.14 zM*#y0G%`R(p#snY1`4os>j5ZI4+;D-r0^iBR8$zGVa4iT!hi@!9S+d2AV2~GTrW%r
   26.15 zumC}U017-f0>D57f(%GUqRWsozy#4qO9w!Z04W6zO$<;#I>6!LVbcsRZu~e01`Q1Y
   26.16 zY(P*M!IA?U5ST7afdfzk(g7G4AdWTA2?(fLpUnX&htJCq+|6*{w*y(}gr9T_82|zU
   26.17 zMsq74uOobb>epqrU3aY|;2(gqWyeAPX$)Wo6h&1r246E!{kF*iTXlusQk4OvfqMUO
   26.18 z;K7KnffpcB8t`;LUEd+#h=~by#h`-$V8GsRvP^(Zh3-9&0Rc!=)j(afapr*m8FglW
   26.19 z29r5*Kwklj*hGpw{S?5HPC_YVQUf$`UlSEj(4K?>S!mCS$>ngtWH?xGQw2*%0F(d~
   26.20 zY*1rsX)Q69I1mAKoSnycr~!&<HGx9}8B73*nT!JW00jdSrA#(5ag-1R?o2?zrZII|
   26.21 z69yMJlw(4kmRjlnm^PryHjTE*i7}gM<B34%%py=Wvd*e2tvWvSUO>ABJF7RbiX&gE
   26.22 w7Cv*$ILbm54M5{qW6gWl@YpODRMJY@EVMi#>+QGThAZy4<d$nLRv`cYJ0sYC#{d8T
   26.23 
    27.1 new file mode 100644
    27.2 --- /dev/null
    27.3 +++ b/skins/calendar/event_view.dtml
    27.4 @@ -0,0 +1,173 @@
    27.5 +<dtml-var standard_html_header>
    27.6 +
    27.7 +<STYLE>
    27.8 +
    27.9 +<!--
   27.10 +
   27.11 +body,td,th { font-family:arial,helvetica; font-size:10pt } 
   27.12 +
   27.13 +BIG {FONT-FAMILY: Arial,Helvetica; font-size: 17px;font-weight:bold}
   27.14 +
   27.15 +A {FONT-FAMILY: Arial,Helvetica; font-size: 13.5px;}
   27.16 +
   27.17 +SMALL {FONT-FAMILY: Arial,Helvetica; font-size: 2.75mm;}
   27.18 +
   27.19 + .bttn 
   27.20 +
   27.21 + { 
   27.22 +
   27.23 +   BACKGROUND-COLOR: #cccccc; 
   27.24 +
   27.25 +   BORDER-BOTTOM: #9999cc 2px outset; 
   27.26 +
   27.27 +   BORDER-LEFT: #9999cc 2px outset; 
   27.28 +
   27.29 +   BORDER-RIGHT: #9999cc 2px outset; 
   27.30 +
   27.31 +   BORDER-TOP: #9999cc 2px outset 
   27.32 +
   27.33 + } 
   27.34 +
   27.35 +   -->
   27.36 +
   27.37 +</STYLE>
   27.38 +
   27.39 +<style type="text/css" >
   27.40 +
   27.41 +<!--
   27.42 +   td.EventBackground  { background: #DDDDDD }
   27.43 +   
   27.44 +   td.EventHorizon  { background: #818384 }
   27.45 +
   27.46 +   a.calendarlight:link { color: #FFFFFF; text-decoration: none }
   27.47 +
   27.48 +   a.calendarlight:visited { color: #FFFFFF; text-decoration: none }
   27.49 +
   27.50 +   a.calendarlight:active { color: #FFFFFF; text-decoration: none }
   27.51 +
   27.52 +   a.calendarlight:hover { color: #dddddd; text-decoration: none }
   27.53 +
   27.54 +   a.calendardark:link { color: #FFFFFF; text-decoration: none }
   27.55 +
   27.56 +   a.calendardark:visited { color: #FFFFFF; text-decoration: none }
   27.57 +
   27.58 +   a.calendardark:active { color: #FFFFFF; text-decoration: none }
   27.59 +
   27.60 +   a.calendardark:hover { color: #dddddd; text-decoration: none }
   27.61 +
   27.62 +   #nav a:link { color: blue; text-decoration: none } 
   27.63 +
   27.64 +   #nav a:visited { color: blue; text-decoration: none } 
   27.65 +
   27.66 +   #nav a:active { color: blue; text-decoration: none } 
   27.67 +
   27.68 +   #nav a:hover { color: #000000; text-decoration: none } 
   27.69 +
   27.70 +   #nav TD {PADDING-LEFT: 2px;} 
   27.71 +
   27.72 +-->
   27.73 +
   27.74 +</style>
   27.75 +
   27.76 +<center>
   27.77 +<table border=0 bordercolor=bronze width=100% height=100% >
   27.78 +<tr><td align=center valign=top >
   27.79 +<table border=0 bordercolor=grey width=100% height=100% cellspacing=0 cellpadding=0 >
   27.80 +<tr height=2% ><td colspan=5 >
   27.81 +<table border=0 cellpadding=0 cellspacing=0 >
   27.82 +<tr><td><image src="event_info_tab.gif" height=21 width=90 border=0></td><td>
   27.83 +<img src="space.gif" width=5 height=1></td>
   27.84 +<td></td>
   27.85 +<td><img src="space.gif" width=5 height=1></td>
   27.86 +<td></td>
   27.87 +<td><img src="space.gif" width=5 height=1></td>
   27.88 +<td></td>
   27.89 +<td><img src="space.gif" width=5 height=1></td>
   27.90 +</tr></table>
   27.91 +</td></tr>
   27.92 +<tr height=2% ><td Class="EventHorizon" width=15% colspan=4 >&nbsp;</td>
   27.93 +<td width=85% Class="EventHorizon" align=right nowrap >
   27.94 +<table border=0 cellpadding=6 cellspacing=0>
   27.95 +<tr><td align="left" nowrap>
   27.96 +&nbsp;
   27.97 +</td></tr>
   27.98 +</table>
   27.99 +</td></tr>
  27.100 +<tr height=1>
  27.101 +<td colspan=5 bgcolor=#FFFFFF ><img src="space.gif" width=10 height=1 border=0></td>
  27.102 +</tr>
  27.103 +<tr height=96% >
  27.104 +<td colspan=5 Class="EventBackground" valign=top>
  27.105 +<table border=0 cellpadding=5 width=100% cellspacing=3>
  27.106 +<tr>
  27.107 +<td Class="EventBackground">
  27.108 +<table border=0 width=100% >
  27.109 +<TR>
  27.110 +<th width="10%" align=left NOWRAP>Event Name</th>
  27.111 +<TD NOWRAP>&dtml-Title;</TD>
  27.112 +<th width="10%" align=left NOWRAP>Contact Name</th>
  27.113 +<TD NOWRAP>&dtml-contact_name;</TD>
  27.114 +</TR>
  27.115 +<TR>
  27.116 +<th width="10%" align=left NOWRAP>Location</th>
  27.117 +<TD NOWRAP>&dtml-location;</TD>
  27.118 +<th width="10%" align=left NOWRAP>Contact Email</th>
  27.119 +<TD NOWRAP><dtml-if contact_email><a href="mailto:&dtml-contact_email;">&dtml-contact_email;</a></dtml-if></TD>
  27.120 +</TR>
  27.121 +<TR>
  27.122 +<th width="10%" align=left NOWRAP>Event type</th>
  27.123 +<TD VALIGN=top NOWRAP><dtml-var expr="_.string.join(Subject(), ' ')"></TD>
  27.124 +<th width="10%" align=left NOWRAP>Contact Phone</th>
  27.125 +<TD NOWRAP>&dtml-contact_phone;</TD>
  27.126 +</TR>
  27.127 +
  27.128 +<dtml-if event_url>
  27.129 +<tr>
  27.130 +<th align="left" width="10%" NOWRAP>Event URL</th>
  27.131 +<td colspan="3" NOWRAP><a href="&dtml-event_url;">&dtml-event_url;</a></td>
  27.132 +</tr>
  27.133 +</dtml-if>
  27.134 +<tr>
  27.135 +<td colspan="4" >
  27.136 +<hr>
  27.137 +</td>
  27.138 +</tr>
  27.139 +<tr><th valign="middle" align="left" width="10%" NOWRAP>Start Date</th>
  27.140 +   <td> 
  27.141 +	<dtml-var start fmt=Date>	
  27.142 +		</td>
  27.143 +
  27.144 +   <th valign="middle" align="left" width="10%" NOWRAP> Stop Date </th> 
  27.145 +   <td> 
  27.146 +        <dtml-var end fmt=Date>
  27.147 +              </td>
  27.148 +
  27.149 +   </td>
  27.150 +</td>
  27.151 +</tr>
  27.152 +<TR>
  27.153 +<th valign="Middle" align="left" width="10%" NOWRAP>Start Time</th>
  27.154 +<TD>
  27.155 +	<dtml-var start fmt=AMPMMinutes>
  27.156 +</TD>
  27.157 +<th valign="middle" align="left" width="10%" NOWRAP>Stop Time</th>
  27.158 +   <td>
  27.159 +     <dtml-var end fmt=AMPMMinutes>
  27.160 +   </td>
  27.161 +</tr>
  27.162 +<tr><td colspan=4><hr></td></tr>
  27.163 +<tr>
  27.164 +<th align=right valign=top >Description</th>
  27.165 +<TD colspan="3" VALIGN=top NOWRAP>&dtml-Description;</TEXTAREA>
  27.166 +</td></tr>
  27.167 +<tr><td height="30" colspan=4>&nbsp;</td></tr>
  27.168 +</table>
  27.169 +</td></tr>
  27.170 +</table>
  27.171 +</td></tr>
  27.172 +</table>
  27.173 +</td></tr>
  27.174 +</table>
  27.175 +	</center>
  27.176 +
  27.177 +<dtml-var standard_html_footer>
    28.1 new file mode 100644
    28.2 index 0000000000000000000000000000000000000000..2799b45c6591f1db05c8c00bd1fd0c5c01f57614
    28.3 GIT binary patch
    28.4 literal 43
    28.5 rc${<hbhEHbWMp7uXkcLY|NlP&1B2pE76uT|0TCb>1|}vKMh0sD$dv~8
    28.6 
    29.1 new file mode 100644
    29.2 --- /dev/null
    29.3 +++ b/skins/zpt_calendar/CalendarStyle.css
    29.4 @@ -0,0 +1,132 @@
    29.5 +.CalendarArrow {
    29.6 +    font-weight: bold;
    29.7 +    text-decoration: none;
    29.8 +    color: #000000;
    29.9 +}
   29.10 +
   29.11 +.CalendarTitle {
   29.12 +    font-weight: bold;
   29.13 +    text-decoration: none;
   29.14 +    text-align: center;
   29.15 +    color: #000000;
   29.16 +}
   29.17 +
   29.18 +div.CalendarBox {
   29.19 +    background-color: white;
   29.20 +    border: None;
   29.21 +    }
   29.22 +
   29.23 +table.calendar {
   29.24 +    border: 1px solid Black;
   29.25 +    text-align: right;
   29.26 +}
   29.27 +
   29.28 +table.calendar th {
   29.29 +    background-color: #DDDDDD;
   29.30 +    font-weight: bold;
   29.31 +    text-align: center;
   29.32 +}
   29.33 +
   29.34 +table.calendar td {
   29.35 +    white-space: nowrap;
   29.36 +    background-color: white;
   29.37 +    width: 1.5em;
   29.38 +}
   29.39 +
   29.40 +table.calendar a {
   29.41 +    text-decoration: none;
   29.42 +    color: Blue;
   29.43 +}
   29.44 +
   29.45 +table.calendar a:hover {
   29.46 +    text-decoration: none;
   29.47 +}
   29.48 +
   29.49 +table.calendar td.weekdays {
   29.50 +    background-color: #AAAAAA;
   29.51 +    border: Black;
   29.52 +    border-style: solid none;
   29.53 +    text-align: center;
   29.54 +}
   29.55 +
   29.56 +table.calendar td.event {
   29.57 +    background-color: #DDDDDD;
   29.58 +    font-weight: bold;
   29.59 +}
   29.60 +
   29.61 +table.calendar td.todayevent {
   29.62 +    background-color: #DDDDDD;
   29.63 +    border: 2px solid Orange;
   29.64 +    font-weight: bold;
   29.65 +}
   29.66 +
   29.67 +table.calendar td.todaynoevent {
   29.68 +    border-collapse: collapse;
   29.69 +    border: 2px solid Orange;
   29.70 +}
   29.71 +
   29.72 +div.day {
   29.73 +    background-color: #FFFFBB;
   29.74 +    border: 1px solid Black;
   29.75 +    visibility: hidden;
   29.76 +    width: 12em;
   29.77 +    z-index: 2;
   29.78 +}
   29.79 +
   29.80 +
   29.81 +div.dayViewBox {
   29.82 +    background-color: white;
   29.83 +    border: None;
   29.84 +    }
   29.85 +
   29.86 +table.dayView {
   29.87 +    border: 1px solid Black;
   29.88 +    text-align: right;
   29.89 +}
   29.90 +
   29.91 +table.dayView th {
   29.92 +    background-color: #DDDDDD;
   29.93 +    font-weight: bold;
   29.94 +    font-size: 75%;
   29.95 +    text-align: center;
   29.96 +}
   29.97 +
   29.98 +table.dayView td {
   29.99 +    white-space: nowrap;
  29.100 +    background-color: white;
  29.101 +    font-size: 75%;
  29.102 +    width: 1.5em;
  29.103 +    padding: 0.2em;
  29.104 +    text-align: center;
  29.105 +}
  29.106 +
  29.107 +table.dayView td.startDate {
  29.108 +    text-align: right;
  29.109 +}
  29.110 +
  29.111 +table.dayView td.endDate {
  29.112 +    text-align: left;
  29.113 +}
  29.114 +
  29.115 +table.dayView td.heading {
  29.116 +    background-color: #AAAAAA;
  29.117 +    border: 1px solid Black;
  29.118 +    text-align: center;
  29.119 +}
  29.120 +
  29.121 +table.Event {
  29.122 +    background-color: #DDDDDD;
  29.123 +}
  29.124 +
  29.125 +table.Event th {
  29.126 +    text-align: left;
  29.127 +    white-space: nowrap;
  29.128 +    font-size: 80%;
  29.129 +    font-weight: bold;
  29.130 +    font-family: Verdana, Helvetica, sans-serif;
  29.131 +}
  29.132 +
  29.133 +table.Event td {
  29.134 +    font-size: 80%;
  29.135 +    font-family: Verdana, Helvetica, sans-serif;
  29.136 +}
    30.1 new file mode 100644
    30.2 --- /dev/null
    30.3 +++ b/skins/zpt_calendar/calendarBox.pt
    30.4 @@ -0,0 +1,64 @@
    30.5 +<html metal:use-macro="context/main_template/macros/master">
    30.6 +<body>
    30.7 +
    30.8 +<metal:slot metal:fill-slot="header">&nbsp;</metal:slot>
    30.9 +
   30.10 +<metal:slot metal:fill-slot="main">
   30.11 + <div metal:define-macro="calendarBox"
   30.12 +     class="CalendarBox"
   30.13 +     tal:define="yearmonth python:context.getMonthAndYear();
   30.14 +                 year python:yearmonth[0];
   30.15 +                 month python:yearmonth[1];
   30.16 +                 weeks python:context.portal_calendar.getEventsForCalendar(month=month, year=year);">
   30.17 +
   30.18 +    <div metal:define-slot="title" class="CalendarTitle">CMF Calendar</div>
   30.19 +
   30.20 +    <!-- The calendar, rendered as a table -->
   30.21 +    <table cellspacing="0" cellpadding="0" border="1">
   30.22 +    <tr><td>
   30.23 +
   30.24 +    <table cellpadding="2" cellspacing="0" border="0" class="calendar">
   30.25 +
   30.26 +        <!-- The headers. The month with links either side -->
   30.27 +        <tr>
   30.28 +            <th>
   30.29 +                <a href="#" class="CalendarArrow" tal:attributes="href python:context.getPreviousMonthLink(request.URL0, month, year)">&laquo;</a>
   30.30 +            </th>
   30.31 +            <th colspan="5" tal:define="date python:DateTime(int(year), int(month), 1)">
   30.32 +              <span tal:replace="python:date.strftime('%B').capitalize()"/> <span tal:replace="python:date.year()"/>
   30.33 +            </th>
   30.34 +            <th>
   30.35 +                <a href="#" class="CalendarArrow" tal:attributes="href python:context.getNextMonthLink(request.URL0, month, year)">&raquo;</a>
   30.36 +            </th>
   30.37 +        </tr>
   30.38 +
   30.39 +        <!-- The week days across the top -->
   30.40 +        <tr tal:define="weekdays context/portal_calendar/getDays">
   30.41 +          <tal:weekday tal:repeat="weekday weekdays">
   30.42 +            <td class="weekdays" tal:content="weekday">Su</td>
   30.43 +          </tal:weekday>
   30.44 +        </tr>
   30.45 +
   30.46 +        <!-- The actual days with in the weeks -->
   30.47 +        <tr tal:repeat="week weeks">
   30.48 +         <tal:loop tal:repeat="day week"
   30.49 +        ><td class="event"
   30.50 +            tal:define="daynumber day/day;
   30.51 +                        datestring python:'%d/%0.2d/%0.2d' % (year, month, daynumber);"
   30.52 +            tal:attributes="class python:context.getDaysClass(daynumber, month, year, day['event'])">
   30.53 +          <a href
   30.54 +             tal:omit-tag="not: day/event"
   30.55 +             tal:attributes="href string:${portal_url}/calendar_day_view?date=${datestring}"
   30.56 +             tal:content="python:daynumber or default"> &nbsp;</a>
   30.57 +         </td></tal:loop>
   30.58 +        </tr>
   30.59 +
   30.60 +    </table>
   30.61 +    </td></tr>
   30.62 +    </table>
   30.63 +
   30.64 + </div>
   30.65 +</metal:slot>
   30.66 +
   30.67 +</body>
   30.68 +</html>
    31.1 new file mode 100644
    31.2 --- /dev/null
    31.3 +++ b/skins/zpt_calendar/calendar_day_view.pt
    31.4 @@ -0,0 +1,49 @@
    31.5 +<html metal:use-macro="context/main_template/macros/master">
    31.6 +<body>
    31.7 +
    31.8 +<metal:slot metal:fill-slot="header">&nbsp;</metal:slot>
    31.9 +
   31.10 +<metal:slot metal:fill-slot="main">
   31.11 + <div metal:define-macro="dayViewBox"
   31.12 +      class="dayViewBox"
   31.13 +      tal:define="thisDay python:DateTime(request.get('date', DateTime().aCommon()[:12]))">
   31.14 +
   31.15 +  <table class="dayView" cellpadding="0" cellspacing="0" border="0">
   31.16 +
   31.17 +   <tr>
   31.18 +     <th>
   31.19 +       <a href="#" tal:attributes="href python:context.getPreviousDayLink(template.absolute_url(), thisDay)">&laquo;</a>
   31.20 +     </th>
   31.21 +     <th tal:content="python:thisDay.aCommon()[:12]" colspan="3" nowrap>Date Heading</th>
   31.22 +     <th>
   31.23 +       <a href="#" tal:attributes="href python:context.getNextDayLink(template.absolute_url(), thisDay)">&raquo;</a>
   31.24 +     </th>
   31.25 +   </tr>
   31.26 +
   31.27 +   <tal:events tal:repeat="event python:context.portal_calendar.getEventsForThisDay(thisDay)">
   31.28 +     <tr>
   31.29 +       <td class="heading" colspan="5" nowrap>
   31.30 +         <a href="#" tal:attributes="href event/getURL" tal:content="event/Title">
   31.31 +           event title
   31.32 +         </a>
   31.33 +       </td>
   31.34 +     </tr>
   31.35 +
   31.36 +     <tr>
   31.37 +       <td class="startDate" tal:content="python:context.getStartAsString(thisDay, event)" nowrap colspan="2">
   31.38 +        Event Start
   31.39 +       </td>
   31.40 +       <td nowrap>
   31.41 +        --
   31.42 +       </td>
   31.43 +       <td class="endDate" tal:content="python:context.getEndAsString(thisDay, event)" nowrap colspan="2">
   31.44 +        Event End
   31.45 +       </td>
   31.46 +     </tr>
   31.47 +   </tal:events>
   31.48 +
   31.49 + </div>
   31.50 +</metal:slot>
   31.51 +
   31.52 +</body>
   31.53 +</html>
    32.1 new file mode 100644
    32.2 --- /dev/null
    32.3 +++ b/skins/zpt_calendar/event_edit_control.py
    32.4 @@ -0,0 +1,12 @@
    32.5 +##parameters=title=None, description=None, event_type=None, effectiveDay=None, effectiveMo=None, effectiveYear=None, expirationDay=None, expirationMo=None, expirationYear=None, start_time=None, startAMPM=None, stop_time=None, stopAMPM=None, location=None, contact_name=None, contact_email=None, contact_phone=None, event_url=None, **kw
    32.6 +##
    32.7 +from Products.CMFCalendar.exceptions import ResourceLockedError
    32.8 +
    32.9 +try:
   32.10 +    context.edit(title, description, event_type, effectiveDay, effectiveMo,
   32.11 +                 effectiveYear, expirationDay, expirationMo, expirationYear,
   32.12 +                 start_time, startAMPM, stop_time, stopAMPM, location,
   32.13 +                 contact_name, contact_email, contact_phone, event_url)
   32.14 +    return context.setStatus(True, 'Event changed.')
   32.15 +except ResourceLockedError, errmsg:
   32.16 +    return context.setStatus(False, errmsg)
    33.1 new file mode 100644
    33.2 --- /dev/null
    33.3 +++ b/skins/zpt_calendar/event_edit_form.py
    33.4 @@ -0,0 +1,23 @@
    33.5 +##parameters=change='', change_and_view=''
    33.6 +##
    33.7 +form = context.REQUEST.form
    33.8 +if change and \
    33.9 +        context.event_edit_control(**form) and \
   33.10 +        context.setRedirect(context, 'object/edit'):
   33.11 +    return
   33.12 +elif change_and_view and \
   33.13 +        context.event_edit_control(**form) and \
   33.14 +        context.setRedirect(context, 'object/view'):
   33.15 +    return
   33.16 +
   33.17 +
   33.18 +options = {}
   33.19 +
   33.20 +buttons = []
   33.21 +target = context.getActionInfo('object/edit')['url']
   33.22 +buttons.append( {'name': 'change', 'value': 'Change'} )
   33.23 +buttons.append( {'name': 'change_and_view', 'value': 'Change and View'} )
   33.24 +options['form'] = { 'action': target,
   33.25 +                    'listButtonInfos': tuple(buttons) }
   33.26 +
   33.27 +return context.event_edit_template(**options)
    34.1 new file mode 100644
    34.2 --- /dev/null
    34.3 +++ b/skins/zpt_calendar/event_edit_template.pt
    34.4 @@ -0,0 +1,188 @@
    34.5 +<html metal:use-macro="context/main_template/macros/master">
    34.6 +<body>
    34.7 +
    34.8 +<metal:slot metal:fill-slot="header" i18n:domain="cmf_default">
    34.9 +<h1 i18n:translate="">Edit: <tal:span
   34.10 +    tal:content="context/Title" i18n:name="obj_title">Title</tal:span></h1>
   34.11 +</metal:slot>
   34.12 +
   34.13 +<metal:slot metal:fill-slot="main" i18n:domain="cmf_default"
   34.14 +   tal:define="form options/form">
   34.15 +<div class="Desktop">
   34.16 +
   34.17 +<form action="event_edit_form" method="post"
   34.18 +   tal:attributes="action form/action">
   34.19 +<table class="FormLayout">
   34.20 + <tr>
   34.21 +  <th i18n:translate="">Event Name</th>
   34.22 +  <td>
   34.23 +   <input type="text"
   34.24 +      name="title" maxlength="100" size="35" value="Title"
   34.25 +      tal:attributes="value context/Title">
   34.26 +  </td>
   34.27 +  <th i18n:translate="">Contact Name</th>
   34.28 +  <td>
   34.29 +   <input type="text"
   34.30 +      name="contact_name" maxlength="100" size="35" value="contact_name"
   34.31 +      tal:attributes="value context/contact_name">
   34.32 +  </td>
   34.33 + </tr>
   34.34 + <tr>
   34.35 +  <th i18n:translate="">Location</th>
   34.36 +  <td>
   34.37 +   <input type="text"
   34.38 +      name="location" maxlength="100" size="35" value="location"
   34.39 +      tal:attributes="value context/location">
   34.40 +  </td>
   34.41 +  <th i18n:translate="">Contact Email</th>
   34.42 +  <td>
   34.43 +   <input type="text"
   34.44 +      name="contact_email" maxlength="100" size="35" value="contact_email"
   34.45 +      tal:attributes="value context/contact_email">
   34.46 +  </td>
   34.47 + </tr>
   34.48 + <tr>
   34.49 +  <th i18n:translate="">Event type</th>
   34.50 +  <td>
   34.51 +   <select name="event_type:list" multiple
   34.52 +      tal:define="contentSubject context/Subject;
   34.53 +                  allowedSubjects python:context.portal_metadata.listAllowedSubjects(context)">
   34.54 +    <option tal:replace="nothing">Event Type 1</option>
   34.55 +    <option tal:replace="nothing">Event Type 2</option>
   34.56 +    <option tal:replace="nothing">...</option>
   34.57 +    <option value="subj"
   34.58 +       tal:repeat="subj allowedSubjects"
   34.59 +       tal:attributes="value subj; selected python:subj in contentSubject"
   34.60 +       tal:content="subj">Event Type N</option>
   34.61 +   </select>
   34.62 +  </td>
   34.63 +  <th i18n:translate="">Contact Phone</th>
   34.64 +  <td>
   34.65 +   <input type="text" name="contact_phone" maxlength="100" size="35"
   34.66 +      value="contact_phone" id="cb_contact_phone"
   34.67 +      tal:attributes="value context/contact_phone" />
   34.68 +  </td>
   34.69 + </tr>
   34.70 + <tr>
   34.71 +  <th i18n:translate="">Event URL</th>
   34.72 +  <td colspan="3">
   34.73 +   <input type="text" name="event_url" size="55" maxlength="100"
   34.74 +      value="event_url"
   34.75 +      tal:attributes="value context/event_url" />
   34.76 +  </td>
   34.77 + </tr>
   34.78 + <tr>
   34.79 +  <th i18n:translate="">Start Date</th>
   34.80 +  <td tal:define="startstrings context/getStartStrings">
   34.81 +   <select name="effectiveYear">
   34.82 +    <option value=""
   34.83 +       tal:define="years context/buildYears"
   34.84 +       tal:repeat="year years"
   34.85 +       tal:attributes="value year; selected python:year == startstrings['year']"
   34.86 +       tal:content="year">Year</option>
   34.87 +   </select>
   34.88 +   &nbsp;
   34.89 +   <select name="effectiveMo">
   34.90 +    <option value=""
   34.91 +       tal:define="months context/buildMonths"
   34.92 +       tal:repeat="month months"
   34.93 +       tal:attributes="value month; selected python:month == startstrings['month']"
   34.94 +       tal:content="month">Month</option>
   34.95 +   </select>
   34.96 +   &nbsp;
   34.97 +   <select name="effectiveDay">
   34.98 +    <option value=""
   34.99 +       tal:define="days context/buildDays"
  34.100 +       tal:repeat="day days"
  34.101 +       tal:attributes="value day; selected python:day == startstrings['day']"
  34.102 +       tal:content="day">Day</option>
  34.103 +   </select>
  34.104 +  </td>
  34.105 +  <th i18n:translate="">Stop Date</th>
  34.106 +  <td tal:define="endstrings context/getEndStrings">
  34.107 +   <select name="expirationYear">
  34.108 +    <option value=""
  34.109 +       tal:define="years context/buildYears"
  34.110 +       tal:repeat="year years"
  34.111 +       tal:attributes="value year; selected python:year == endstrings['year']"
  34.112 +       tal:content="year">Day</option>
  34.113 +   </select>
  34.114 +   &nbsp;
  34.115 +   <select name="expirationMo">
  34.116 +    <option value=""
  34.117 +       tal:define="months context/buildMonths"
  34.118 +       tal:repeat="month months"
  34.119 +       tal:attributes="value month; selected python:month == endstrings['month']"
  34.120 +       tal:content="month">Month</option>
  34.121 +   </select>
  34.122 +   &nbsp;
  34.123 +   <select name="expirationDay">
  34.124 +    <option value=""
  34.125 +       tal:define="days context/buildDays"
  34.126 +       tal:repeat="day days"
  34.127 +       tal:attributes="value day; selected python:day == endstrings['day']"
  34.128 +       tal:content="day">Day</option>
  34.129 +   </select>
  34.130 +  </td>
  34.131 + </tr>
  34.132 + <tr>
  34.133 +  <th i18n:translate="">Start Time</th>
  34.134 +  <td tal:define="stTimeString python:context.getStartTimeString().split();
  34.135 +                  amSel python:(len(stTimeString) == 2 and stTimeString[1] == 'am');
  34.136 +                  pmSel python:(len(stTimeString) == 2 and stTimeString[1] == 'pm')">
  34.137 +   <select name="start_time">
  34.138 +    <option value=""
  34.139 +       tal:repeat="bt context/buildTimes"
  34.140 +       tal:attributes="value bt; selected python:bt == stTimeString[0]"
  34.141 +       tal:content="bt">start time</option>
  34.142 +   </select>
  34.143 +   &nbsp;
  34.144 +   <input type="radio" name="startAMPM" value="am" id="cb_start_am"
  34.145 +      tal:attributes="checked amSel" />
  34.146 +   <label for="cb_start_am" i18n:translate="">am</label>
  34.147 +   <input type="radio" name="startAMPM" value="pm" id="cb_start_pm"
  34.148 +      tal:attributes="checked pmSel" />
  34.149 +   <label for="cb_start_pm" i18n:translate="">pm</label>
  34.150 +   <input type="hidden" name="startAMPM:default" value="pm" />
  34.151 +  </td>
  34.152 +  <th i18n:translate="">Stop Time</th>
  34.153 +  <td tal:define="stTimeString python:context.getStopTimeString().split();
  34.154 +                  amSel python:(len(stTimeString) == 2 and stTimeString[1] == 'am');
  34.155 +                  pmSel python:(len(stTimeString) == 2 and stTimeString[1] == 'pm')">
  34.156 +   <select name="stop_time">
  34.157 +   <option value=""
  34.158 +      tal:repeat="bt context/buildTimes"
  34.159 +      tal:attributes="value bt; selected python:bt == stTimeString[0]"
  34.160 +      tal:content="bt">end time</option>
  34.161 +   </select>
  34.162 +   &nbsp;
  34.163 +   <input type="radio" name="stopAMPM" value="am" id="cb_stop_am"
  34.164 +      tal:attributes="checked amSel" />
  34.165 +   <label for="cb_stop_am" i18n:translate="">am</label>
  34.166 +   <input type="radio" name="stopAMPM" value="pm" id="cb_stop_pm"
  34.167 +      tal:attributes="checked pmSel" />
  34.168 +   <label for="cb_stop_pm" i18n:translate="">pm</label>
  34.169 +   <input type="hidden" name="stopAMPM:default" value="pm" />
  34.170 +  </td>
  34.171 + </tr>
  34.172 + <tr>
  34.173 +  <th i18n:translate="">Description</th>
  34.174 +  <td class="TextField" colspan="3">
  34.175 +   <textarea name="description:text" rows="5" cols="70" wrap="soft"
  34.176 +      tal:content="context/Description"></textarea>
  34.177 +  </td>
  34.178 + </tr>
  34.179 + <tr>
  34.180 +  <td>&nbsp;</td>
  34.181 +  <td colspan="3">
  34.182 +   <metal:macro metal:use-macro="context/form_widgets/macros/buttons" />
  34.183 +  </td>
  34.184 + </tr>
  34.185 +</table>
  34.186 +</form>
  34.187 +
  34.188 +</div>
  34.189 +</metal:slot>
  34.190 +
  34.191 +</body>
  34.192 +</html>
    35.1 new file mode 100644
    35.2 index 0000000000000000000000000000000000000000..3c3abe6c553554d8e822f9dc2af0a6984f9412ac
    35.3 GIT binary patch
    35.4 literal 926
    35.5 zc$~dc&uh<d7=MvuE1Fsn4s&6&xjgxmT56fOOd_XUXdcw2cC&VwIL)u3<cWibcG&oq
    35.6 z+743YBs~|$i5vM7aC*F6??1x(_Vhf@>v{e7d`_MkIdb$uFM9DCD7~zTDzCCCsLHI2
    35.7 zO0Tp^s2N!i6<%Q#(5z>9lzX|AL#vi$QRZb<1|k)*5Ctz-0Rk2@vAE1>=#E}wdZuMU
    35.8 zXIVvLc!p&_EZFo&_jF5#NO9I8&C@Im<jB=TBs^gWh_W=0G+B}sq(GMlO)M@$f>iT)
    35.9 zghfF5c$SrWxP?P<$%0MuFbf0KlN4tI9<TsphRotN?Vx_h8%ZJ<Ns}dHl@1e|(8PkA
   35.10 zQ=2uPxkE-$AJ4Kfa|3;)T(V%(VGhV<r4pKz(U9x%F0?8;pb(@mge1~XCgKqS7Bm!*
   35.11 zrbBo1z{F@I_3<n#gFX(l_|XDJix4d&v>4EgMza#lEHpdN1fxkpBaKE2?JycN+A><3
   35.12 z_4<+96R-cApSrcU@^Nl%jvpod%iH~T!#dzQ22|*uIKF8X@49x4cAl+X`?kDqe4*#b
   35.13 z@MQO!tv@#6!}9#L!HbLi(|s$GeZ%v;YiIgS-0e3XnCZM+eYpSd^NFsrdk#%~?My%Y
   35.14 z)VX~6(Se)Yn?Elte0ec?_1^f72e0z6?HyXWZokIv5A7Vf(|_>iIgGuZzcjP7xc2tS
   35.15 LjV*)QdvNR@u}6m)
   35.16 
    36.1 new file mode 100644
    36.2 --- /dev/null
    36.3 +++ b/skins/zpt_calendar/event_view.pt
    36.4 @@ -0,0 +1,75 @@
    36.5 +<html xmlns:tal="http://xml.zope.org/namespaces/tal"
    36.6 +      xmlns:metal="http://xml.zope.org/namespaces/metal"
    36.7 +      metal:use-macro="here/main_template/macros/master">
    36.8 +<head>
    36.9 + <metal:block fill-slot="base"
   36.10 + ><tal:span tal:replace="structure here/getBaseTag"
   36.11 +/></metal:block>
   36.12 +</head>
   36.13 +<body>
   36.14 +
   36.15 +<metal:block metal:fill-slot="main">
   36.16 +<div class="Desktop">
   36.17 +
   36.18 +<table class="Event" border="0" cellpadding="5" width="100%" cellspacing="3">
   36.19 + <tr>
   36.20 +  <th width="10%">Event Name</th>
   36.21 +  <td nowrap="nowrap" tal:content="here/Title">Title</td>
   36.22 +  <th width="10%">Contact Name</th>
   36.23 +  <td nowrap="nowrap" tal:content="here/contact_name">contact_name</td>
   36.24 + </tr>
   36.25 + <tr>
   36.26 +  <th width="10%">Location</th>
   36.27 +  <td nowrap="nowrap" tal:content="here/location">location</td>
   36.28 +  <th width="10%">Contact Email</th>
   36.29 +  <td nowrap="nowrap" tal:condition="here/contact_email">
   36.30 +   <a tal:attributes="href python:'mailto:' + here.contact_email"
   36.31 +      tal:content="here/contact_email"
   36.32 +      href="mailto:contact_email">contact_email</a></td>
   36.33 + </tr>
   36.34 + <tr>
   36.35 +  <th width="10%">Event type</th>
   36.36 +  <td valign="top" nowrap="nowrap"
   36.37 +     tal:content="python:' '.join( here.Subject() )"></td>
   36.38 +  <th width="10%">Contact Phone</th>
   36.39 +  <td nowrap="nowrap" tal:content="here/contact_phone">contact_phone</td>
   36.40 + </tr>
   36.41 + <tr tal:condition="here/event_url|nothing">
   36.42 +  <th width="10%">Event URL</th>
   36.43 +  <td colspan="3" nowrap="nowrap"><a href="event_url"
   36.44 +     tal:attributes="href here/event_url"
   36.45 +     tal:content="here/event_url">event_url</a></td>
   36.46 + </tr>
   36.47 + <tr>
   36.48 +  <td colspan="4"><hr /></td>
   36.49 + </tr>
   36.50 + <tr>
   36.51 +  <th width="10%">Start Date</th>
   36.52 +  <td tal:content="python:DateTime.Date(here.start())">start</td>
   36.53 +  <th width="10%">Stop Date</th>
   36.54 +  <td tal:content="python:DateTime.Date(here.end())">end</td>
   36.55 + </tr>
   36.56 + <tr>
   36.57 +  <th width="10%">Start Time</th>
   36.58 +  <td tal:content="python:DateTime.Time(here.start())">start</td>
   36.59 +  <th width="10%">Stop Time</th>
   36.60 +  <td tal:content="python:DateTime.Time(here.end())">end</td>
   36.61 + </tr>
   36.62 + <tr>
   36.63 +  <td colspan="4"><hr /></td>
   36.64 + </tr>
   36.65 + <tr>
   36.66 +  <th width="10%">Description</th>
   36.67 +  <td colspan="3" valign="top" nowrap="nowrap"
   36.68 +     tal:content="here/Description">Description</td>
   36.69 + </tr>
   36.70 + <tr>
   36.71 +  <td colspan="4">&nbsp;</td>
   36.72 + </tr>
   36.73 +</table>
   36.74 +
   36.75 +</div>
   36.76 +</metal:block>
   36.77 +
   36.78 +</body>
   36.79 +</html>
    37.1 new file mode 100644
    37.2 --- /dev/null
    37.3 +++ b/skins/zpt_calendar/getDaysClass.py
    37.4 @@ -0,0 +1,21 @@
    37.5 +##parameters=day, month, year, event=None
    37.6 +
    37.7 +# Is the date given today?
    37.8 +# If so we want to return a class of 'todayevent' so that the event gets the
    37.9 +# today border around it.
   37.10 +# If not we want to return just 'event' so the event gets the proper shading.
   37.11 +
   37.12 +import DateTime
   37.13 +
   37.14 +current = DateTime.DateTime()
   37.15 +
   37.16 +if current.year()==year and current.month()==month and current.day()==int(day):
   37.17 +  if event:
   37.18 +    return "todayevent"
   37.19 +  else:
   37.20 +    return "todaynoevent"
   37.21 +  
   37.22 +if event:
   37.23 +  return "event"
   37.24 +else:
   37.25 +  return ""
   37.26 \ No newline at end of file
    38.1 new file mode 100644
    38.2 --- /dev/null
    38.3 +++ b/skins/zpt_calendar/getEndAsString.py
    38.4 @@ -0,0 +1,15 @@
    38.5 +##parameters=thisDay, event
    38.6 +
    38.7 +# Returns a string to represent the event
    38.8 +
    38.9 +from DateTime import DateTime
   38.10 +
   38.11 +text = ""
   38.12 +
   38.13 +last_date=DateTime(thisDay.Date()+" 23:59:59")
   38.14 +
   38.15 +if event.end > last_date:
   38.16 +  return event.end.aCommon()[:12]
   38.17 +else:
   38.18 +  return event.end.TimeMinutes()
   38.19 +
    39.1 new file mode 100644
    39.2 --- /dev/null
    39.3 +++ b/skins/zpt_calendar/getMonthAndYear.py
    39.4 @@ -0,0 +1,34 @@
    39.5 +
    39.6 +# Get the year and month that the calendar should display.
    39.7 +
    39.8 +import DateTime
    39.9 +
   39.10 +current = DateTime.DateTime()
   39.11 +
   39.12 +year = None
   39.13 +month = None
   39.14 +use_session = container.portal_calendar.getUseSession()
   39.15 +
   39.16 +# First priority goes to the data in the request
   39.17 +year  = context.REQUEST.get('year',  None)
   39.18 +month = context.REQUEST.get('month', None)
   39.19 +session = None
   39.20 +
   39.21 +# Next get the data from the SESSION
   39.22 +if use_session == "True":
   39.23 +    session = context.REQUEST.get('SESSION', None)
   39.24 +    if session:
   39.25 +        if not year:   year  = session.get('calendar_year',  None)
   39.26 +        if not month:  month = session.get('calendar_month', None)
   39.27 +  
   39.28 +# Last resort to Today
   39.29 +if not year:   year  = current.year()
   39.30 +if not month:  month = current.month()
   39.31 +
   39.32 +# Then store the results in the session for next time
   39.33 +if session:
   39.34 +  session.set('calendar_year',  year)
   39.35 +  session.set('calendar_month', month)
   39.36 +  
   39.37 +# Finally return the results
   39.38 +return (year, month)
    40.1 new file mode 100644
    40.2 --- /dev/null
    40.3 +++ b/skins/zpt_calendar/getNextDayLink.py
    40.4 @@ -0,0 +1,12 @@
    40.5 +##parameters=base_url, thisDay
    40.6 +
    40.7 +# Takes a base url and returns a link to the previous day
    40.8 +
    40.9 +thisDay += 1
   40.10 +
   40.11 +x = '%s?date=%s' % (
   40.12 +                    base_url,
   40.13 +                    thisDay.Date()
   40.14 +                    )
   40.15 +
   40.16 +return x
   40.17 \ No newline at end of file
    41.1 new file mode 100644
    41.2 --- /dev/null
    41.3 +++ b/skins/zpt_calendar/getNextMonthLink.py
    41.4 @@ -0,0 +1,12 @@
    41.5 +##parameters=base_url, month, year
    41.6 +
    41.7 +nextMonthTime = container.portal_calendar.getNextMonth(month, year)
    41.8 +
    41.9 +# Takes a base url and returns a link to the previous month
   41.10 +x = '%s?month:int=%d&year:int=%d' % (
   41.11 +                                     base_url,
   41.12 +                                     nextMonthTime.month(),
   41.13 +                                     nextMonthTime.year()
   41.14 +                                     )
   41.15 +
   41.16 +return x
   41.17 \ No newline at end of file
    42.1 new file mode 100644
    42.2 --- /dev/null
    42.3 +++ b/skins/zpt_calendar/getPreviousDayLink.py
    42.4 @@ -0,0 +1,12 @@
    42.5 +##parameters=base_url, thisDay
    42.6 +
    42.7 +# Takes a base url and returns a link to the next day
    42.8 +
    42.9 +thisDay -= 1
   42.10 +
   42.11 +x = '%s?date=%s' % (
   42.12 +                    base_url,
   42.13 +                    thisDay.Date()
   42.14 +                    )
   42.15 +
   42.16 +return x
   42.17 \ No newline at end of file
    43.1 new file mode 100644
    43.2 --- /dev/null
    43.3 +++ b/skins/zpt_calendar/getPreviousMonthLink.py
    43.4 @@ -0,0 +1,12 @@
    43.5 +##parameters=base_url, month, year
    43.6 +
    43.7 +prevMonthTime = container.portal_calendar.getPreviousMonth(month, year)
    43.8 +
    43.9 +# Takes a base url and returns a link to the previous month
   43.10 +x = '%s?month:int=%d&year:int=%d' % (
   43.11 +                                     base_url,
   43.12 +                                     prevMonthTime.month(),
   43.13 +                                     prevMonthTime.year()
   43.14 +                                     )
   43.15 +
   43.16 +return x
   43.17 \ No newline at end of file
    44.1 new file mode 100644
    44.2 --- /dev/null
    44.3 +++ b/skins/zpt_calendar/getStartAsString.py
    44.4 @@ -0,0 +1,12 @@
    44.5 +##parameters=thisDay, event
    44.6 +
    44.7 +# Returns a string to represent the start of the event
    44.8 +
    44.9 +from DateTime import DateTime
   44.10 +
   44.11 +first_date=DateTime(thisDay.Date()+" 00:00:00")
   44.12 +
   44.13 +if event.start < first_date:
   44.14 +  return event.start.aCommon()[:12]
   44.15 +else:
   44.16 +  return event.start.TimeMinutes()
    45.1 new file mode 100644
    45.2 --- /dev/null
    45.3 +++ b/tests/__init__.py
    45.4 @@ -0,0 +1,6 @@
    45.5 +"""\
    45.6 +Unit test package for CMFCalendar.
    45.7 +
    45.8 +As test suites are added, they should be added to the
    45.9 +mega-test-suite in Products.CMFCalendar.tests.test_all.py
   45.10 +"""
    46.1 new file mode 100644
    46.2 --- /dev/null
    46.3 +++ b/tests/test_Calendar.py
    46.4 @@ -0,0 +1,656 @@
    46.5 +##############################################################################
    46.6 +#
    46.7 +# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
    46.8 +#
    46.9 +# This software is subject to the provisions of the Zope Public License,
   46.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   46.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   46.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   46.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   46.14 +# FOR A PARTICULAR PURPOSE.
   46.15 +#
   46.16 +##############################################################################
   46.17 +""" Unit tests for CalendarTool module.
   46.18 +
   46.19 +$Id: test_Calendar.py 66245 2006-03-28 17:02:36Z jens $
   46.20 +"""
   46.21 +
   46.22 +import unittest
   46.23 +import Testing
   46.24 +try:
   46.25 +    import Zope2
   46.26 +except ImportError:
   46.27 +    # BBB: for Zope 2.7
   46.28 +    import Zope as Zope2
   46.29 +Zope2.startup()
   46.30 +
   46.31 +import locale
   46.32 +
   46.33 +from AccessControl.SecurityManagement import newSecurityManager
   46.34 +from AccessControl.SecurityManagement import noSecurityManager
   46.35 +from AccessControl.User import UnrestrictedUser
   46.36 +from DateTime import DateTime
   46.37 +from Products.ExternalMethod.ExternalMethod import manage_addExternalMethod
   46.38 +from Products.TemporaryFolder.TemporaryFolder import MountedTemporaryFolder
   46.39 +from Products.Transience.Transience import TransientObjectContainer
   46.40 +from Products.CMFCore.tests.base.testcase import WarningInterceptor
   46.41 +from Products.CMFDefault.factory import addConfiguredSite
   46.42 +from Testing.makerequest import makerequest
   46.43 +try:
   46.44 +    import transaction
   46.45 +except ImportError:
   46.46 +    # BBB: for Zope 2.7
   46.47 +    from Products.CMFCore.utils import transaction
   46.48 +from Products.CMFCore import Skinnable
   46.49 +
   46.50 +
   46.51 +class CalendarTests(unittest.TestCase):
   46.52 +
   46.53 +    def _makeOne(self, *args, **kw):
   46.54 +        from Products.CMFCalendar.CalendarTool import CalendarTool
   46.55 +
   46.56 +        return CalendarTool(*args, **kw)
   46.57 +
   46.58 +    def test_new(self):
   46.59 +        ctool = self._makeOne()
   46.60 +        self.assertEqual( ctool.getId(), 'portal_calendar' )
   46.61 +
   46.62 +    def test_types(self):
   46.63 +        ctool = self._makeOne()
   46.64 +        self.assertEqual(ctool.getCalendarTypes(), ('Event',))
   46.65 +
   46.66 +        ctool.edit_configuration(show_types=['Event','Party'],
   46.67 +                                 show_states=[],
   46.68 +                                 use_session="")
   46.69 +        self.assertEqual( ctool.getCalendarTypes(), ('Event', 'Party') )
   46.70 +
   46.71 +    def test_states(self):
   46.72 +        ctool = self._makeOne()
   46.73 +        self.assertEqual(ctool.getCalendarStates(), ('published',))
   46.74 +
   46.75 +        ctool.edit_configuration(show_types=[],
   46.76 +                                 show_states=['pending', 'published'],
   46.77 +                                 use_session="")
   46.78 +        self.assertEqual( ctool.getCalendarStates(), ('pending', 'published') )
   46.79 +
   46.80 +    def test_days(self):
   46.81 +        ctool = self._makeOne()
   46.82 +        old_locale = locale.getlocale(locale.LC_ALL)[0]
   46.83 +        locale.setlocale(locale.LC_ALL, 'C')
   46.84 +        try:
   46.85 +            self.assertEqual( ctool.getDays(),
   46.86 +                              ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] )
   46.87 +        finally:
   46.88 +            locale.setlocale(locale.LC_ALL, old_locale)
   46.89 +
   46.90 +
   46.91 +class CalendarRequestTests(unittest.TestCase, WarningInterceptor):
   46.92 +
   46.93 +    def setUp(self):
   46.94 +        self._trap_warning_output()
   46.95 +        self._oldSkindata = Skinnable.SKINDATA.copy()
   46.96 +        transaction.begin()
   46.97 +
   46.98 +        app = self.app = makerequest(Zope2.app())
   46.99 +        # Log in as a god :-)
  46.100 +        newSecurityManager(None,
  46.101 +                           UnrestrictedUser('god', 'god', ['Manager'], '') )
  46.102 +
  46.103 +        #app.manage_addProduct['CMFDefault'].manage_addCMFSite('CalendarTest')
  46.104 +        addConfiguredSite(app, 'CalendarTest', 'CMFDefault:default')
  46.105 +
  46.106 +        self.Site = app.CalendarTest
  46.107 +
  46.108 +        manage_addExternalMethod(app.CalendarTest,
  46.109 +                                 id='install_events',
  46.110 +                                 title="Install Events",
  46.111 +                                 module="CMFCalendar.Install",
  46.112 +                                 function="install")
  46.113 +
  46.114 +        ExMethod = app.restrictedTraverse('/CalendarTest/install_events')
  46.115 +        ExMethod()
  46.116 +        self.Tool = app.CalendarTest.portal_calendar
  46.117 +
  46.118 +        self.Site.clearCurrentSkin()
  46.119 +        self.Site.setupCurrentSkin(app.REQUEST)
  46.120 +
  46.121 +        # sessioning setup
  46.122 +        if getattr(app, 'temp_folder', None) is None:
  46.123 +            temp_folder = MountedTemporaryFolder('temp_folder')
  46.124 +            app._setObject('temp_folder', temp_folder)
  46.125 +        if getattr(app.temp_folder, 'session_data', None) is None:
  46.126 +            session_data = TransientObjectContainer('session_data')
  46.127 +            app.temp_folder._setObject('session_data', session_data)
  46.128 +        app.REQUEST.set_lazy( 'SESSION',
  46.129 +                              app.session_data_manager.getSessionData )
  46.130 +
  46.131 +    def tearDown(self):
  46.132 +        self.app.REQUEST.close()
  46.133 +        noSecurityManager()
  46.134 +        transaction.abort()
  46.135 +        self.app._p_jar.close()
  46.136 +        Skinnable.SKINDATA = self._oldSkindata
  46.137 +        self._free_warning_output()
  46.138 +
  46.139 +    def _testURL(self,url,params=None):
  46.140 +        Site = self.Site
  46.141 +        obj = Site.restrictedTraverse(url)
  46.142 +        if params is None:
  46.143 +            params=(obj, Site.REQUEST)
  46.144 +        obj(*params)
  46.145 +
  46.146 +    def test_sessions(self):
  46.147 +        self.Tool.edit_configuration(show_types=['Event'], use_session="True")
  46.148 +
  46.149 +        self._testURL('/CalendarTest/calendarBox', ())
  46.150 +
  46.151 +        self.failUnless(self.app.REQUEST.SESSION.get('calendar_year',None))
  46.152 +
  46.153 +    def test_noSessions(self):
  46.154 +        self.Tool.edit_configuration(show_types=['Event'], use_session="")
  46.155 +
  46.156 +        self._testURL('/CalendarTest/calendarBox', ())
  46.157 +
  46.158 +        self.failIf(self.app.REQUEST.SESSION.get('calendar_year',None))
  46.159 +
  46.160 +    def test_simpleCalendarRendering(self):
  46.161 +        data = [
  46.162 +                [
  46.163 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.164 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.165 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.166 +                 {'day': 1, 'event': 0, 'eventslist':[]},
  46.167 +                 {'day': 2, 'event': 0, 'eventslist':[]},
  46.168 +                 {'day': 3, 'event': 0, 'eventslist':[]},
  46.169 +                 {'day': 4, 'event': 0, 'eventslist':[]},
  46.170 +                 ],
  46.171 +                [
  46.172 +                 {'day': 5, 'event': 0, 'eventslist':[]},
  46.173 +                 {'day': 6, 'event': 0, 'eventslist':[]},
  46.174 +                 {'day': 7, 'event': 0, 'eventslist':[]},
  46.175 +                 {'day': 8, 'event': 0, 'eventslist':[]},
  46.176 +                 {'day': 9, 'event': 0, 'eventslist':[]},
  46.177 +                 {'day':10, 'event': 0, 'eventslist':[]},
  46.178 +                 {'day':11, 'event': 0, 'eventslist':[]},
  46.179 +                 ],
  46.180 +                [
  46.181 +                 {'day':12, 'event': 0, 'eventslist':[]},
  46.182 +                 {'day':13, 'event': 0, 'eventslist':[]},
  46.183 +                 {'day':14, 'event': 0, 'eventslist':[]},
  46.184 +                 {'day':15, 'event': 0, 'eventslist':[]},
  46.185 +                 {'day':16, 'event': 0, 'eventslist':[]},
  46.186 +                 {'day':17, 'event': 0, 'eventslist':[]},
  46.187 +                 {'day':18, 'event': 0, 'eventslist':[]},
  46.188 +                 ],
  46.189 +                [
  46.190 +                 {'day':19, 'event': 0, 'eventslist':[]},
  46.191 +                 {'day':20, 'event': 0, 'eventslist':[]},
  46.192 +                 {'day':21, 'event': 0, 'eventslist':[]},
  46.193 +                 {'day':22, 'event': 0, 'eventslist':[]},
  46.194 +                 {'day':23, 'event': 0, 'eventslist':[]},
  46.195 +                 {'day':24, 'event': 0, 'eventslist':[]},
  46.196 +                 {'day':25, 'event': 0, 'eventslist':[]},
  46.197 +                 ],
  46.198 +                [
  46.199 +                 {'day':26, 'event': 0, 'eventslist':[]},
  46.200 +                 {'day':27, 'event': 0, 'eventslist':[]},
  46.201 +                 {'day':28, 'event': 0, 'eventslist':[]},
  46.202 +                 {'day':29, 'event': 0, 'eventslist':[]},
  46.203 +                 {'day':30, 'event': 0, 'eventslist':[]},
  46.204 +                 {'day':31, 'event': 0, 'eventslist':[]},
  46.205 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.206 +                 ]
  46.207 +                ]
  46.208 +        result = self.Tool.getEventsForCalendar(month='5', year='2002')
  46.209 +        self.assertEqual(result, data)
  46.210 +
  46.211 +    def test_singleEventCalendarRendering(self):
  46.212 +
  46.213 +        self.Site.Members.invokeFactory(type_name="Event",id='Event1')
  46.214 +        event = self.app.restrictedTraverse('/CalendarTest/Members/Event1')
  46.215 +        event.edit( title='title'
  46.216 +                    , description='description'
  46.217 +                    , eventType=( 'eventType', )
  46.218 +                    , effectiveDay=1
  46.219 +                    , effectiveMo=5
  46.220 +                    , effectiveYear=2002
  46.221 +                    , expirationDay=1
  46.222 +                    , expirationMo=5
  46.223 +                    , expirationYear=2002
  46.224 +                    , start_time="00:00"
  46.225 +                    , startAMPM="AM"
  46.226 +                    , stop_time="11:59"
  46.227 +                    , stopAMPM="PM"
  46.228 +                    )
  46.229 +        self.Site.portal_workflow.doActionFor(
  46.230 +                                              event,
  46.231 +                                              'publish',
  46.232 +                                              comment='testing')
  46.233 +
  46.234 +        data = [
  46.235 +                [
  46.236 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.237 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.238 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.239 +                 {'day': 1, 'event': 1, 'eventslist':[{'title': 'title', 'end': '23:59:00', 'start': '00:00:00'}]},
  46.240 +                 {'day': 2, 'event': 0, 'eventslist':[]},
  46.241 +                 {'day': 3, 'event': 0, 'eventslist':[]},
  46.242 +                 {'day': 4, 'event': 0, 'eventslist':[]},
  46.243 +                 ],
  46.244 +                [
  46.245 +                 {'day': 5, 'event': 0, 'eventslist':[]},
  46.246 +                 {'day': 6, 'event': 0, 'eventslist':[]},
  46.247 +                 {'day': 7, 'event': 0, 'eventslist':[]},
  46.248 +                 {'day': 8, 'event': 0, 'eventslist':[]},
  46.249 +                 {'day': 9, 'event': 0, 'eventslist':[]},
  46.250 +                 {'day':10, 'event': 0, 'eventslist':[]},
  46.251 +                 {'day':11, 'event': 0, 'eventslist':[]},
  46.252 +                 ],
  46.253 +                [
  46.254 +                 {'day':12, 'event': 0, 'eventslist':[]},
  46.255 +                 {'day':13, 'event': 0, 'eventslist':[]},
  46.256 +                 {'day':14, 'event': 0, 'eventslist':[]},
  46.257 +                 {'day':15, 'event': 0, 'eventslist':[]},
  46.258 +                 {'day':16, 'event': 0, 'eventslist':[]},
  46.259 +                 {'day':17, 'event': 0, 'eventslist':[]},
  46.260 +                 {'day':18, 'event': 0, 'eventslist':[]},
  46.261 +                 ],
  46.262 +                [
  46.263 +                 {'day':19, 'event': 0, 'eventslist':[]},
  46.264 +                 {'day':20, 'event': 0, 'eventslist':[]},
  46.265 +                 {'day':21, 'event': 0, 'eventslist':[]},
  46.266 +                 {'day':22, 'event': 0, 'eventslist':[]},
  46.267 +                 {'day':23, 'event': 0, 'eventslist':[]},
  46.268 +                 {'day':24, 'event': 0, 'eventslist':[]},
  46.269 +                 {'day':25, 'event': 0, 'eventslist':[]},
  46.270 +                 ],
  46.271 +                [
  46.272 +                 {'day':26, 'event': 0, 'eventslist':[]},
  46.273 +                 {'day':27, 'event': 0, 'eventslist':[]},
  46.274 +                 {'day':28, 'event': 0, 'eventslist':[]},
  46.275 +                 {'day':29, 'event': 0, 'eventslist':[]},
  46.276 +                 {'day':30, 'event': 0, 'eventslist':[]},
  46.277 +                 {'day':31, 'event': 0, 'eventslist':[]},
  46.278 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.279 +                 ]
  46.280 +                ]
  46.281 +        result = self.Tool.getEventsForCalendar(month='5', year='2002')
  46.282 +        self.assertEqual(result, data)
  46.283 +
  46.284 +    def test_eventCalendarRenderingIssue411(self):
  46.285 +        #  http://www.zope.org/Collectors/CMF/411
  46.286 +        self.Site.Members.invokeFactory(type_name="Event",id='Event1')
  46.287 +        event = self.app.restrictedTraverse('/CalendarTest/Members/Event1')
  46.288 +        event.edit( title='title'
  46.289 +                    , description='description'
  46.290 +                    , eventType=( 'eventType', )
  46.291 +                    , effectiveDay=31
  46.292 +                    , effectiveMo=3
  46.293 +                    , effectiveYear=2006
  46.294 +                    , expirationDay=1
  46.295 +                    , expirationMo=4
  46.296 +                    , expirationYear=2006
  46.297 +                    , start_time="00:00"
  46.298 +                    , startAMPM="AM"
  46.299 +                    , stop_time="00:00"
  46.300 +                    , stopAMPM="AM"
  46.301 +                    )
  46.302 +        self.Site.portal_workflow.doActionFor(
  46.303 +                                              event,
  46.304 +                                              'publish',
  46.305 +                                              comment='testing')
  46.306 +
  46.307 +        self.Site.Members.invokeFactory(type_name="Event",id='Event2')
  46.308 +        event = self.app.restrictedTraverse('/CalendarTest/Members/Event2')
  46.309 +        event.edit( title='title'
  46.310 +                    , description='description'
  46.311 +                    , eventType=( 'eventType', )
  46.312 +                    , effectiveDay=29
  46.313 +                    , effectiveMo=3
  46.314 +                    , effectiveYear=2006
  46.315 +                    , expirationDay=30
  46.316 +                    , expirationMo=3
  46.317 +                    , expirationYear=2006
  46.318 +                    , start_time="00:00"
  46.319 +                    , startAMPM="AM"
  46.320 +                    , stop_time="00:00"
  46.321 +                    , stopAMPM="AM"
  46.322 +                    )
  46.323 +        self.Site.portal_workflow.doActionFor(
  46.324 +                                              event,
  46.325 +                                              'publish',
  46.326 +                                              comment='testing')
  46.327 +
  46.328 +        # With the bug unfixed, this raises a TypeError
  46.329 +        ignored = self.Site.portal_calendar.catalog_getevents(2006, 3)
  46.330 +
  46.331 +
  46.332 +    def test_spanningEventCalendarRendering(self):
  46.333 +
  46.334 +        self.Site.Members.invokeFactory(type_name="Event",id='Event1')
  46.335 +        event = self.app.restrictedTraverse('/CalendarTest/Members/Event1')
  46.336 +        event.edit( title='title'
  46.337 +                    , description='description'
  46.338 +                    , eventType=( 'eventType', )
  46.339 +                    , effectiveDay=1
  46.340 +                    , effectiveMo=5
  46.341 +                    , effectiveYear=2002
  46.342 +                    , expirationDay=31
  46.343 +                    , expirationMo=5
  46.344 +                    , expirationYear=2002
  46.345 +                    , start_time="00:00"
  46.346 +                    , startAMPM="AM"
  46.347 +                    , stop_time="11:59"
  46.348 +                    , stopAMPM="PM"
  46.349 +                    )
  46.350 +        self.Site.portal_workflow.doActionFor(
  46.351 +                                              event,
  46.352 +                                              'publish',
  46.353 +                                              comment='testing')
  46.354 +
  46.355 +        data = [
  46.356 +                [
  46.357 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.358 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.359 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.360 +                 {'day': 1, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': '00:00:00'}]},
  46.361 +                 {'day': 2, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.362 +                 {'day': 3, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.363 +                 {'day': 4, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.364 +                 ],
  46.365 +                [
  46.366 +                 {'day': 5, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.367 +                 {'day': 6, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.368 +                 {'day': 7, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.369 +                 {'day': 8, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.370 +                 {'day': 9, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.371 +                 {'day':10, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.372 +                 {'day':11, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.373 +                 ],
  46.374 +                [
  46.375 +                 {'day':12, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.376 +                 {'day':13, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.377 +                 {'day':14, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.378 +                 {'day':15, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.379 +                 {'day':16, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.380 +                 {'day':17, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.381 +                 {'day':18, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.382 +                 ],
  46.383 +                [
  46.384 +                 {'day':19, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.385 +                 {'day':20, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.386 +                 {'day':21, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.387 +                 {'day':22, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.388 +                 {'day':23, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.389 +                 {'day':24, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.390 +                 {'day':25, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.391 +                 ],
  46.392 +                [
  46.393 +                 {'day':26, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.394 +                 {'day':27, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.395 +                 {'day':28, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.396 +                 {'day':29, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.397 +                 {'day':30, 'event': 1, 'eventslist':[{'title': 'title', 'end': None, 'start': None}]},
  46.398 +                 {'day':31, 'event': 1, 'eventslist':[{'title': 'title', 'end': '23:59:00', 'start': None}]},
  46.399 +                 {'day': 0, 'event': 0, 'eventslist':[]},
  46.400 +                 ]
  46.401 +                ]
  46.402 +        result = self.Tool.getEventsForCalendar(month='5', year='2002')
  46.403 +        self.assertEqual(result, data)
  46.404 +
  46.405 +    def test_getPreviousMonth(self):
  46.406 +        self.assertEqual( self.Tool.getPreviousMonth(2,2002),
  46.407 +                          DateTime('2002/1/1') )
  46.408 +        self.assertEqual( self.Tool.getPreviousMonth(1,2002),
  46.409 +                          DateTime('2001/12/1') )
  46.410 +
  46.411 +    def test_getNextMonth(self):
  46.412 +        self.assertEqual( self.Tool.getNextMonth(12,2001),
  46.413 +                          DateTime('2002/1/1') )
  46.414 +        self.assertEqual( self.Tool.getNextMonth(1,2002),
  46.415 +                          DateTime('2002/2/1') )
  46.416 +
  46.417 +    def test_getBeginAndEndTimes(self):
  46.418 +        self.assertEqual( self.Tool.getBeginAndEndTimes(1,12,2001),
  46.419 +                          ( DateTime('2001/12/1 12:00:00AM'),
  46.420 +                            DateTime('2001/12/1 11:59:59PM') ) )
  46.421 +
  46.422 +    def test_singleDayRendering(self):
  46.423 +        wftool = self.Site.portal_workflow
  46.424 +
  46.425 +        self.Site.Members.invokeFactory(type_name="Event",id='Event1')
  46.426 +        event = self.Site.Members.Event1
  46.427 +        event.edit( title='title'
  46.428 +                    , description='description'
  46.429 +                    , eventType=( 'eventType', )
  46.430 +                    , effectiveDay=1
  46.431 +                    , effectiveMo=5
  46.432 +                    , effectiveYear=2002
  46.433 +                    , expirationDay=31
  46.434 +                    , expirationMo=5
  46.435 +                    , expirationYear=2002
  46.436 +                    , start_time="00:00"
  46.437 +                    , startAMPM="AM"
  46.438 +                    , stop_time="11:59"
  46.439 +                    , stopAMPM="PM"
  46.440 +                    )
  46.441 +        wftool.doActionFor(event, 'publish', comment='testing')
  46.442 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/1'))
  46.443 +        self.assertEqual( len(events), 1 )
  46.444 +
  46.445 +        self.Site.Members.invokeFactory(type_name="Event",id='Event2')
  46.446 +        event = self.Site.Members.Event2
  46.447 +        event.edit( title='title'
  46.448 +                    , description='description'
  46.449 +                    , eventType=( 'eventType', )
  46.450 +                    , effectiveDay=1
  46.451 +                    , effectiveMo=5
  46.452 +                    , effectiveYear=2002
  46.453 +                    , expirationDay=1
  46.454 +                    , expirationMo=5
  46.455 +                    , expirationYear=2002
  46.456 +                    , start_time="00:00"
  46.457 +                    , startAMPM="AM"
  46.458 +                    , stop_time="11:59"
  46.459 +                    , stopAMPM="PM"
  46.460 +                    )
  46.461 +        wftool.doActionFor(event, 'publish', comment='testing')
  46.462 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/1'))
  46.463 +        self.assertEqual( len(events), 2 )
  46.464 +
  46.465 +        self.Site.Members.invokeFactory(type_name="Event",id='Event3')
  46.466 +        event = self.Site.Members.Event3
  46.467 +        event.edit( title='title'
  46.468 +                    , description='description'
  46.469 +                    , eventType=( 'eventType', )
  46.470 +                    , effectiveDay=12
  46.471 +                    , effectiveMo=12
  46.472 +                    , effectiveYear=2001
  46.473 +                    , expirationDay=1
  46.474 +                    , expirationMo=5
  46.475 +                    , expirationYear=2002
  46.476 +                    , start_time="00:00"
  46.477 +                    , startAMPM="AM"
  46.478 +                    , stop_time="11:59"
  46.479 +                    , stopAMPM="PM"
  46.480 +                    )
  46.481 +        wftool.doActionFor(event, 'publish', comment='testing')
  46.482 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/1'))
  46.483 +        self.assertEqual( len(events), 3 )
  46.484 +
  46.485 +        self.Site.Members.invokeFactory(type_name="Event",id='Event4')
  46.486 +        event = self.Site.Members.Event4
  46.487 +        event.edit( title='title'
  46.488 +                    , description='description'
  46.489 +                    , eventType=( 'eventType', )
  46.490 +                    , effectiveDay=12
  46.491 +                    , effectiveMo=12
  46.492 +                    , effectiveYear=2001
  46.493 +                    , expirationDay=31
  46.494 +                    , expirationMo=5
  46.495 +                    , expirationYear=2002
  46.496 +                    , start_time="00:00"
  46.497 +                    , startAMPM="AM"
  46.498 +                    , stop_time="11:59"
  46.499 +                    , stopAMPM="PM"
  46.500 +                    )
  46.501 +        wftool.doActionFor(event, 'publish', comment='testing')
  46.502 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/1'))
  46.503 +        self.assertEqual( len(events), 4 )
  46.504 +
  46.505 +        self.Site.Members.invokeFactory(type_name="Event",id='Event5')
  46.506 +        event = self.Site.Members.Event5
  46.507 +        event.edit( title='title'
  46.508 +                    , description='description'
  46.509 +                    , eventType=( 'eventType', )
  46.510 +                    , effectiveDay=31
  46.511 +                    , effectiveMo=5
  46.512 +                    , effectiveYear=2002
  46.513 +                    , expirationDay=31
  46.514 +                    , expirationMo=5
  46.515 +                    , expirationYear=2002
  46.516 +                    , start_time="00:00"
  46.517 +                    , startAMPM="AM"
  46.518 +                    , stop_time="11:59"
  46.519 +                    , stopAMPM="PM"
  46.520 +                    )
  46.521 +        wftool.doActionFor(event, 'publish', comment='testing')
  46.522 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/1'))
  46.523 +        self.assertEqual( len(events), 4 )
  46.524 +        events = self.Tool.getEventsForThisDay(thisDay=DateTime('2002/5/31'))
  46.525 +        self.assertEqual( len(events), 3 )
  46.526 +
  46.527 +    def test_lastDayRendering(self):
  46.528 +        # Bug in catalog_getevents included events starting at 00:00:00 on the next day
  46.529 +
  46.530 +        self.Site.invokeFactory('Event', id='today', title='today',
  46.531 +                                 start_date='2002/05/31 23:50:00', 
  46.532 +                                 end_date='2002/05/31 23:59:59')
  46.533 +
  46.534 +        self.Site.invokeFactory('Event', id='tomorrow', title='tomorrow',
  46.535 +                                 start_date='2002/06/01 00:00:00', 
  46.536 +                                 end_date='2002/06/01 00:10:00')
  46.537 +
  46.538 +        self.Site.portal_workflow.doActionFor(self.Site.today, 'publish')
  46.539 +        self.Site.portal_workflow.doActionFor(self.Site.tomorrow, 'publish')
  46.540 +
  46.541 +        # Last week of May 2002
  46.542 +        data = [
  46.543 +               {'day': 25, 'event': 0, 'eventslist':[]},
  46.544 +               {'day': 26, 'event': 0, 'eventslist':[]},
  46.545 +               {'day': 27, 'event': 0, 'eventslist':[]},
  46.546 +               {'day': 28, 'event': 0, 'eventslist':[]},
  46.547 +               {'day': 29, 'event': 0, 'eventslist':[]},
  46.548 +               {'day': 30, 'event': 0, 'eventslist':[]},
  46.549 +               {'day': 31, 'event': 1, 'eventslist':[{'start': '23:50:00', 'end': '23:59:59', 'title': 'today'}]},
  46.550 +               ]
  46.551 +
  46.552 +        events = self.Site.portal_calendar.catalog_getevents(2002, 5)
  46.553 +        self.assertEqual([events[e] for e in range(25, 32)], data)
  46.554 +
  46.555 +    def test_firstDayRendering(self):
  46.556 +        # Double check it works on the other boundary as well
  46.557 +
  46.558 +        self.Site.invokeFactory('Event', id='yesterday', title='yesterday',
  46.559 +                                 start_date='2002/05/31 23:50:00', 
  46.560 +                                 end_date='2002/05/31 23:59:59')
  46.561 +
  46.562 +        self.Site.invokeFactory('Event', id='today', title='today',
  46.563 +                                 start_date='2002/06/01 00:00:00', 
  46.564 +                                 end_date='2002/06/01 00:10:00')
  46.565 +
  46.566 +        self.Site.portal_workflow.doActionFor(self.Site.yesterday, 'publish')
  46.567 +        self.Site.portal_workflow.doActionFor(self.Site.today, 'publish')
  46.568 +
  46.569 +        # First week of June 2002
  46.570 +        data = [
  46.571 +               {'day': 1, 'event': 1, 'eventslist':[{'start': '00:00:00', 'end': '00:10:00', 'title': 'today'}]},
  46.572 +               {'day': 2, 'event': 0, 'eventslist':[]},
  46.573 +               {'day': 3, 'event': 0, 'eventslist':[]},
  46.574 +               {'day': 4, 'event': 0, 'eventslist':[]},
  46.575 +               {'day': 5, 'event': 0, 'eventslist':[]},
  46.576 +               {'day': 6, 'event': 0, 'eventslist':[]},
  46.577 +               {'day': 7, 'event': 0, 'eventslist':[]},
  46.578 +               ]
  46.579 +
  46.580 +        events = self.Site.portal_calendar.catalog_getevents(2002, 6)
  46.581 +        self.assertEqual([events[e] for e in range(1, 8)], data)
  46.582 +
  46.583 +    def test_workflowStateRendering(self):
  46.584 +        # Calendar should return events in all of the selected workflow states
  46.585 +
  46.586 +        self.Site.invokeFactory('Event', id='meeting',
  46.587 +                                 start_date='2002/05/01 11:00:00', 
  46.588 +                                 end_date='2002/05/01 13:30:00')
  46.589 +
  46.590 +        self.Site.invokeFactory('Event', id='dinner',
  46.591 +                                 start_date='2002/05/01 20:00:00', 
  46.592 +                                 end_date='2002/05/01 22:00:00')
  46.593 +
  46.594 +        self.assertEqual(len(self.Site.portal_catalog(portal_type='Event')), 2)
  46.595 +
  46.596 +        # No published events
  46.597 +        self.assertEqual(len(self.Site.portal_calendar.getEventsForThisDay(DateTime('2002/05/01'))), 0) 
  46.598 +        
  46.599 +        # One published event
  46.600 +        self.Site.portal_workflow.doActionFor(self.Site.meeting, 'publish')
  46.601 +        self.assertEqual(len(self.Site.portal_catalog(review_state='published')), 1)
  46.602 +
  46.603 +        self.assertEqual(len(self.Site.portal_calendar.getEventsForThisDay(DateTime('2002/05/01'))), 1) 
  46.604 +
  46.605 +        # One pending event
  46.606 +        self.Site.portal_workflow.doActionFor(self.Site.dinner, 'submit')
  46.607 +        self.assertEqual(len(self.Site.portal_catalog(review_state='pending')), 1)
  46.608 +
  46.609 +        self.assertEqual(len(self.Site.portal_calendar.getEventsForThisDay(DateTime('2002/05/01'))), 1) 
  46.610 +
  46.611 +        # Make calendar return pending events
  46.612 +        self.Site.portal_calendar.edit_configuration(show_types=('Event',), 
  46.613 +                                                     show_states=('pending', 'published'), 
  46.614 +                                                     use_session='')
  46.615 +
  46.616 +        self.assertEqual(len(self.Site.portal_calendar.getEventsForThisDay(DateTime('2002/05/01'))), 2)
  46.617 +
  46.618 +    def test_EventEndingMidnight(self):
  46.619 +        # Events ending exactly at midnight should not be shown for the day
  46.620 +        # after (see http://www.zope.org/Collectors/CMF/246)
  46.621 +        cal = self.Site.portal_calendar
  46.622 +        the_day = DateTime('2002/05/01')
  46.623 +        day_after = DateTime('2002/05/02')
  46.624 +
  46.625 +        self.Site.invokeFactory( 'Event'
  46.626 +                               , id='party'
  46.627 +                               , start_date=the_day
  46.628 +                               , end_date=day_after
  46.629 +                               )
  46.630 +        self.Site.portal_workflow.doActionFor(self.Site.party, 'publish')
  46.631 +
  46.632 +        # One entry should be present for the day of the event
  46.633 +        self.assertEqual(len(cal.getEventsForThisDay(the_day)), 1)
  46.634 +
  46.635 +        # No entry should be present for the day after
  46.636 +        self.assertEqual(len(cal.getEventsForThisDay(day_after)), 0)
  46.637 +
  46.638 +        # First week of May 2002
  46.639 +        data = [
  46.640 +               {'day': 1, 'event': 1, 'eventslist':[{'start': '00:00:00', 'end': '23:59:59', 'title': 'party'}]},
  46.641 +               {'day': 2, 'event': 0, 'eventslist':[]},
  46.642 +               {'day': 3, 'event': 0, 'eventslist':[]},
  46.643 +               {'day': 4, 'event': 0, 'eventslist':[]},
  46.644 +               {'day': 5, 'event': 0, 'eventslist':[]},
  46.645 +               {'day': 6, 'event': 0, 'eventslist':[]},
  46.646 +               {'day': 7, 'event': 0, 'eventslist':[]},
  46.647 +               ]
  46.648 +
  46.649 +        events = self.Site.portal_calendar.catalog_getevents(2002, 5)
  46.650 +        self.assertEqual([events[e] for e in range(1, 8)], data)
  46.651 +
  46.652 +
  46.653 +def test_suite():
  46.654 +    return unittest.TestSuite((
  46.655 +        unittest.makeSuite(CalendarTests),
  46.656 +        unittest.makeSuite(CalendarRequestTests),
  46.657 +        ))
  46.658 +
  46.659 +if __name__ == '__main__':
  46.660 +    unittest.main(defaultTest='test_suite')
    47.1 new file mode 100644
    47.2 --- /dev/null
    47.3 +++ b/tests/test_Event.py
    47.4 @@ -0,0 +1,156 @@
    47.5 +##############################################################################
    47.6 +#
    47.7 +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
    47.8 +#
    47.9 +# This software is subject to the provisions of the Zope Public License,
   47.10 +# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
   47.11 +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   47.12 +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   47.13 +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   47.14 +# FOR A PARTICULAR PURPOSE.
   47.15 +#
   47.16 +##############################################################################
   47.17 +""" Unit tests for Event module.
   47.18 +
   47.19 +$Id: test_Event.py 38418 2005-09-09 08:40:13Z yuppie $
   47.20 +"""
   47.21 +
   47.22 +from unittest import TestCase, TestSuite, makeSuite, main
   47.23 +import Testing
   47.24 +try:
   47.25 +    import Zope2
   47.26 +except ImportError: # BBB: for Zope 2.7
   47.27 +    import Zope as Zope2
   47.28 +Zope2.startup()
   47.29 +
   47.30 +from DateTime import DateTime
   47.31 +
   47.32 +from Products.CMFCore.tests.base.dummy import DummyTool
   47.33 +from Products.CMFCore.tests.base.testcase import RequestTest
   47.34 +
   47.35 +
   47.36 +class TestEvent(TestCase):
   47.37 +
   47.38 +    def _makeOne(self, id, *args, **kw):
   47.39 +        from Products.CMFCalendar.Event import Event
   47.40 +
   47.41 +        return Event(id, *args, **kw)
   47.42 +
   47.43 +    def test_z2interfaces(self):
   47.44 +        from Interface.Verify import verifyClass
   47.45 +        from Products.CMFCore.interfaces.Contentish \
   47.46 +                import Contentish as IContentish
   47.47 +        from Products.CMFCore.interfaces.DublinCore \
   47.48 +                import CatalogableDublinCore as ICatalogableDublinCore
   47.49 +        from Products.CMFCore.interfaces.DublinCore \
   47.50 +                import DublinCore as IDublinCore
   47.51 +        from Products.CMFCore.interfaces.DublinCore \
   47.52 +                import MutableDublinCore as IMutableDublinCore
   47.53 +        from Products.CMFCore.interfaces.Dynamic \
   47.54 +                import DynamicType as IDynamicType
   47.55 +        from Products.CMFCalendar.Event import Event
   47.56 +
   47.57 +        verifyClass(ICatalogableDublinCore, Event)
   47.58 +        verifyClass(IContentish, Event)
   47.59 +        verifyClass(IDublinCore, Event)
   47.60 +        verifyClass(IDynamicType, Event)
   47.61 +        verifyClass(IMutableDublinCore, Event)
   47.62 +
   47.63 +    def test_z3interfaces(self):
   47.64 +        try:
   47.65 +            from zope.interface.verify import verifyClass
   47.66 +            from Products.CMFCore.interfaces import ICatalogableDublinCore
   47.67 +            from Products.CMFCore.interfaces import IContentish
   47.68 +            from Products.CMFCore.interfaces import IDublinCore
   47.69 +            from Products.CMFCore.interfaces import IDynamicType
   47.70 +            from Products.CMFCore.interfaces import IMutableDublinCore
   47.71 +        except ImportError:
   47.72 +            # BBB: for Zope 2.7
   47.73 +            return
   47.74 +        from Products.CMFCalendar.Event import Event
   47.75 +
   47.76 +        verifyClass(ICatalogableDublinCore, Event)
   47.77 +        verifyClass(IContentish, Event)
   47.78 +        verifyClass(IDublinCore, Event)
   47.79 +        verifyClass(IDynamicType, Event)
   47.80 +        verifyClass(IMutableDublinCore, Event)
   47.81 +
   47.82 +    def test_new(self):
   47.83 +        event = self._makeOne('test')
   47.84 +
   47.85 +        self.assertEqual( event.getId(), 'test' )
   47.86 +        self.failIf( event.Title() )
   47.87 +
   47.88 +    def test_edit(self):
   47.89 +        # Year month and day were processed in the wrong order
   47.90 +        # Also see http://collector.zope.org/CMF/202
   47.91 +        event = self._makeOne('foo')
   47.92 +        event.edit( title='title'
   47.93 +                  , description='description'
   47.94 +                  , eventType=( 'eventType', )
   47.95 +                  , effectiveDay=1
   47.96 +                  , effectiveMo=5
   47.97 +                  , effectiveYear=1999
   47.98 +                  , expirationDay=31
   47.99 +                  , expirationMo=12
  47.100 +                  , expirationYear=1999
  47.101 +                  , start_time="00:00"
  47.102 +                  , startAMPM="AM"
  47.103 +                  , stop_time="11:59"
  47.104 +                  , stopAMPM="PM"
  47.105 +                  )
  47.106 +
  47.107 +        self.assertEqual( event.Title(), 'title' )
  47.108 +        self.assertEqual( event.Description(), 'description' )
  47.109 +        self.assertEqual( event.Subject(), ( 'eventType', ), event.Subject() )
  47.110 +        self.assertEqual( event.effective_date, None )
  47.111 +        self.assertEqual( event.expiration_date, None )
  47.112 +        self.assertEqual( event.end(), DateTime('1999/12/31 23:59') )
  47.113 +        self.assertEqual( event.start(), DateTime('1999/05/01 00:00') )
  47.114 +        self.failIf( event.contact_name )
  47.115 +
  47.116 +    def test_puke(self):
  47.117 +        event = self._makeOne('shouldPuke')
  47.118 +
  47.119 +        self.assertRaises( DateTime.DateError
  47.120 +                         , event.edit
  47.121 +                         , effectiveDay=31
  47.122 +                         , effectiveMo=2
  47.123 +                         , effectiveYear=1999
  47.124 +                         , start_time="00:00"
  47.125 +                         , startAMPM="AM"
  47.126 +                         )
  47.127 +
  47.128 +
  47.129 +class EventPUTTests(RequestTest):
  47.130 +
  47.131 +    def _makeOne(self, id, *args, **kw):
  47.132 +        from Products.CMFCalendar.Event import Event
  47.133 +
  47.134 +        # NullResource.PUT calls the PUT method on the bare object!
  47.135 +        return Event(id, *args, **kw)
  47.136 +
  47.137 +    def test_PutWithoutMetadata(self):
  47.138 +        self.REQUEST['BODY'] = ''
  47.139 +        d = self._makeOne('foo')
  47.140 +        d.PUT(self.REQUEST, self.RESPONSE)
  47.141 +
  47.142 +        self.assertEqual( d.Title(), '' )
  47.143 +        self.assertEqual( d.Format(), 'text/plain' )
  47.144 +        self.assertEqual( d.Description(), '' )
  47.145 +        self.assertEqual( d.Subject(), () )
  47.146 +        self.assertEqual( d.Contributors(), () )
  47.147 +        self.assertEqual( d.EffectiveDate(), 'None' )
  47.148 +        self.assertEqual( d.ExpirationDate(), 'None' )
  47.149 +        self.assertEqual( d.Language(), '' )
  47.150 +        self.assertEqual( d.Rights(), '' )
  47.151 +
  47.152 +
  47.153 +def test_suite():
  47.154 +    return TestSuite((
  47.155 +        makeSuite(TestEvent),
  47.156 +        makeSuite(EventPUTTests),
  47.157 +        ))
  47.158 +
  47.159 +if __name__ == '__main__':
  47.160 +    main(defaultTest='test_suite')
    48.1 new file mode 100644
    48.2 index 0000000000000000000000000000000000000000..8aa90b53b65ee410b8a9be3819fcba223c936c4e
    48.3 GIT binary patch
    48.4 literal 166
    48.5 zc${<hbhEHb6krfwc+9}CY0HL=j*jye&gSIiPn<a6=+R>pRTa;lKc6*c#>$l|!@|OE
    48.6 z+_;gLl<4i_^&bor|MB@IrlcyAXO?6rxO@5rFev_HVdP@qXV75)0+6W;ERG*edamB8
    48.7 z5Tng@Gc-|#-=T@ALFE+FH0H}Z`3En&SC;VkC&SXpufZbA$LjKNlL8A@=P~XD7D8Nh
    48.8 Qa~U7Jeo%8GoQ1&}0ANHx<p2Nx
    48.9 
    49.1 new file mode 100644
    49.2 --- /dev/null
    49.3 +++ b/version.txt
    49.4 @@ -0,0 +1,1 @@
    49.5 +CMF-1.6.1
    50.1 new file mode 100644
    50.2 --- /dev/null
    50.3 +++ b/www/configureCalendarTool.zpt
    50.4 @@ -0,0 +1,63 @@
    50.5 +<tal:header tal:replace="structure here/manage_page_header">header</tal:header>
    50.6 +<tal:tabs   tal:replace="structure here/manage_tabs">tabs</tal:tabs>
    50.7 +
    50.8 +
    50.9 +<form action="edit_configuration" method="post">
   50.10 +
   50.11 + <h3>Portal Types to show in the calendar</h3>
   50.12 + <select name="show_types:list" size="7" multiple>
   50.13 + 
   50.14 +  <tal:types tal:repeat="type here/portal_types/objectIds">
   50.15 +  
   50.16 +   <option value="#"
   50.17 +           tal:attributes="value type"
   50.18 +           tal:content="type"
   50.19 +           tal:condition="python: type not in here.getCalendarTypes()">type</option>
   50.20 +
   50.21 +   <option value="#" selected
   50.22 +           tal:attributes="value type"
   50.23 +           tal:content="type"
   50.24 +           tal:condition="python: type in here.getCalendarTypes()">type</option>
   50.25 +
   50.26 +  </tal:types>
   50.27 +
   50.28 + </select>
   50.29 + 
   50.30 + <p>
   50.31 + All types that are to show in the calendar must have an attribute 'start' and 
   50.32 + an attribute 'end' which return DateTime objects to the Catalog.
   50.33 + </p>
   50.34 +
   50.35 + <h3>Workflow states to show in the calendar</h3>
   50.36 + <tal:define define="states here/getCalendarStates">
   50.37 +
   50.38 +  <textarea name="show_states:lines" rows="5" cols="20" wrap="off"
   50.39 +            tal:content="python:'\n'.join(states)">state</textarea>
   50.40 +
   50.41 + </tal:define>
   50.42 +
   50.43 + <p>
   50.44 + Each name must be on a separate line.
   50.45 + </p>
   50.46 +
   50.47 + <h3>Preserve selected year and month</h3>
   50.48 + <p>
   50.49 +  <tal:define tal:define="use_session here/portal_calendar/getUseSession">
   50.50 +
   50.51 +    <tal:use_session tal:condition="python:use_session">
   50.52 +      <input type="radio" name="use_session" value="True" checked />Use sessions to remember the calendars state<br />
   50.53 +      <input type="radio" name="use_session" value="" />Don't use sessions to remember the calendars state<br />
   50.54 +    </tal:use_session>
   50.55 +      
   50.56 +    <tal:use_session tal:condition="python:not use_session">
   50.57 +      <input type="radio" name="use_session" value="True" />Use sessions to remember the calendars state<br />
   50.58 +      <input type="radio" name="use_session" value="" checked />Don't use sessions to remember the calendars state<br />
   50.59 +    </tal:use_session>
   50.60 +
   50.61 +  </tal:define>
   50.62 +  <br>
   50.63 +  
   50.64 + <input type="submit" value="Submit">
   50.65 +</form>
   50.66 +
   50.67 +<tal:footer tal:replace="structure here/manage_page_footer">footer</tal:footer>
    51.1 new file mode 100644
    51.2 --- /dev/null
    51.3 +++ b/www/explainCalendarTool.zpt
    51.4 @@ -0,0 +1,10 @@
    51.5 +<tal:header tal:replace="structure here/manage_page_header">header</tal:header>
    51.6 +<tal:tabs   tal:replace="structure here/manage_tabs">tabs</tal:tabs>
    51.7 +
    51.8 +<h3> <code>portal_calendar</code> Tool </h3>
    51.9 +
   51.10 +<p> This tool provides a common interface for providing
   51.11 +    calendar rendering and manipulation functions.
   51.12 +</p>
   51.13 +
   51.14 +<tal:footer tal:replace="structure here/manage_page_footer">footer</tal:footer>