Back to index

plone3  3.1.7
event.py
Go to the documentation of this file.
00001 #  ATContentTypes http://plone.org/products/atcontenttypes/
00002 #  Archetypes reimplementation of the CMF core types
00003 #  Copyright (c) 2003-2006 AT Content Types development team
00004 #
00005 #  This program is free software; you can redistribute it and/or modify
00006 #  it under the terms of the GNU General Public License as published by
00007 #  the Free Software Foundation; either version 2 of the License, or
00008 #  (at your option) any later version.
00009 #
00010 #  This program is distributed in the hope that it will be useful,
00011 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 #  GNU General Public License for more details.
00014 #
00015 #  You should have received a copy of the GNU General Public License
00016 #  along with this program; if not, write to the Free Software
00017 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 #
00019 """
00020 
00021 
00022 """
00023 __author__  = 'Christian Heimes <tiran@cheimes.de>'
00024 __docformat__ = 'restructuredtext'
00025 __old_name__ = 'Products.ATContentTypes.types.ATEvent'
00026 
00027 from types import StringType
00028 
00029 from Products.CMFCore.permissions import ModifyPortalContent, View
00030 from AccessControl import ClassSecurityInfo
00031 from DateTime import DateTime
00032 from ComputedAttribute import ComputedAttribute
00033 
00034 from Products.Archetypes.atapi import Schema
00035 from Products.Archetypes.atapi import DateTimeField
00036 from Products.Archetypes.atapi import LinesField
00037 from Products.Archetypes.atapi import StringField
00038 from Products.Archetypes.atapi import TextField
00039 from Products.Archetypes.atapi import CalendarWidget
00040 from Products.Archetypes.atapi import LinesWidget
00041 from Products.Archetypes.atapi import KeywordWidget
00042 from Products.Archetypes.atapi import RichWidget
00043 from Products.Archetypes.atapi import StringWidget
00044 from Products.Archetypes.atapi import RFC822Marshaller
00045 from Products.Archetypes.atapi import AnnotationStorage
00046 
00047 from Products.ATContentTypes.configuration import zconf
00048 from Products.ATContentTypes.config import PROJECTNAME
00049 from Products.ATContentTypes.content.base import registerATCT
00050 from Products.ATContentTypes.content.base import ATCTContent
00051 from Products.ATContentTypes.interfaces import IATEvent
00052 from Products.ATContentTypes.content.schemata import ATContentTypeSchema
00053 from Products.ATContentTypes.content.schemata import finalizeATCTSchema
00054 from Products.ATContentTypes.lib.calendarsupport import CalendarSupportMixin
00055 from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin
00056 from Products.ATContentTypes.permission import ChangeEvents
00057 from Products.ATContentTypes.utils import DT2dt
00058 
00059 from Products.ATContentTypes import ATCTMessageFactory as _
00060 
00061 ATEventSchema = ATContentTypeSchema.copy() + Schema((
00062     StringField('location',
00063                 searchable=True,
00064                 write_permission = ChangeEvents,
00065                 widget = StringWidget(
00066                     description = '',
00067                     label = _(u'label_event_location', default=u'Event Location')
00068                     )),
00069     DateTimeField('startDate',
00070                   required=True,
00071                   searchable=False,
00072                   accessor='start',
00073                   write_permission = ChangeEvents,
00074                   default_method=DateTime,
00075                   languageIndependent=True,
00076                   widget = CalendarWidget(
00077                         description= '',
00078                         label=_(u'label_event_start', default=u'Event Starts')
00079                         )),
00080 
00081     DateTimeField('endDate',
00082                   required=True,
00083                   searchable=False,
00084                   accessor='end',
00085                   write_permission = ChangeEvents,
00086                   default_method=DateTime,
00087                   languageIndependent=True,
00088                   widget = CalendarWidget(
00089                         description = '',
00090                         label = _(u'label_event_end', default=u'Event Ends')
00091                         )),
00092     TextField('text',
00093               required=False,
00094               searchable=True,
00095               primary=True,
00096               storage = AnnotationStorage(migrate=True),
00097               validators = ('isTidyHtmlWithCleanup',),
00098               default_output_type = 'text/x-html-safe',
00099               widget = RichWidget(
00100                         description = '',
00101                         label = _(u'label_event_announcement', default=u'Event body text'),
00102                         rows = 25,
00103                         allow_file_upload = zconf.ATDocument.allow_document_upload)),
00104 
00105     LinesField('attendees',
00106                languageIndependent=True,
00107                searchable=True,
00108                write_permission=ChangeEvents,
00109                widget=LinesWidget(
00110                       description='',
00111                       label=_(u'label_event_attendees', default=u'Attendees')
00112                       )),
00113 
00114     LinesField('eventType',
00115                required=False,
00116                searchable=True,
00117                write_permission = ChangeEvents,
00118                languageIndependent=True,
00119                widget = KeywordWidget(
00120                         size = 6,
00121                         description='',
00122                         label = _(u'label_event_type', default=u'Event Type(s)')
00123                         )),
00124 
00125     StringField('eventUrl',
00126                 required=False,
00127                 searchable=True,
00128                 accessor='event_url',
00129                 write_permission = ChangeEvents,
00130                 validators=('isURL',),
00131                 widget = StringWidget(
00132                         description = _(u'help_url',
00133                                         default=u"Web address with more info about the event. "
00134                                                  "Add http:// for external links."),
00135                         label = _(u'label_url', default=u'Event URL')
00136                         )),
00137 
00138     StringField('contactName',
00139                 required=False,
00140                 searchable=True,
00141                 accessor='contact_name',
00142                 write_permission = ChangeEvents,
00143                 widget = StringWidget(
00144                         description = '',
00145                         label = _(u'label_contact_name', default=u'Contact Name')
00146                         )),
00147 
00148     StringField('contactEmail',
00149                 required=False,
00150                 searchable=True,
00151                 accessor='contact_email',
00152                 write_permission = ChangeEvents,
00153                 validators = ('isEmail',),
00154                 widget = StringWidget(
00155                         description = '',
00156                         label = _(u'label_contact_email', default=u'Contact E-mail')
00157                         )),
00158     StringField('contactPhone',
00159                 required=False,
00160                 searchable=True,
00161                 accessor='contact_phone',
00162                 write_permission = ChangeEvents,
00163                 validators= (),
00164                 widget = StringWidget(
00165                         description = '',
00166                         label = _(u'label_contact_phone', default=u'Contact Phone')
00167                         )),
00168     ), marshall = RFC822Marshaller()
00169     )
00170 
00171 # Remove the subject field because the eventType field is a proxy for this
00172 ATEventSchema['subject'].widget.visible = {'edit': 'invisible'}
00173 ATEventSchema['subject'].mode = 'r'
00174 
00175 finalizeATCTSchema(ATEventSchema)
00176 # finalizeATCTSchema moves 'location' into 'categories', we move it back:
00177 ATEventSchema.changeSchemataForField('location', 'default')
00178 ATEventSchema.moveField('location', before='startDate')
00179 
00180 class ATEvent(ATCTContent, CalendarSupportMixin, HistoryAwareMixin):
00181     """Information about an upcoming event, which can be displayed in the calendar."""
00182 
00183     schema         =  ATEventSchema
00184 
00185     portal_type    = 'Event'
00186     archetype_name = 'Event'
00187     _atct_newTypeFor = {'portal_type' : 'CMF Event', 'meta_type' : 'CMF Event'}
00188     assocMimetypes = ()
00189     assocFileExt   = ('event', )
00190     cmf_edit_kws   = ('effectiveDay', 'effectiveMo', 'effectiveYear',
00191                       'expirationDay', 'expirationMo', 'expirationYear',
00192                       'start_time', 'startAMPM', 'stop_time', 'stopAMPM',
00193                       'start_date', 'end_date', 'contact_name', 'contact_email',
00194                       'contact_phone', 'event_url')
00195 
00196     __implements__ = (ATCTContent.__implements__, IATEvent,
00197                       CalendarSupportMixin.__implements__,
00198                       HistoryAwareMixin.__implements__)
00199 
00200     security       = ClassSecurityInfo()
00201 
00202     security.declareProtected(ChangeEvents, 'setEventType')
00203     def setEventType(self, value, alreadySet=False, **kw):
00204         """CMF compatibility method
00205 
00206         Changing the event type changes also the subject.
00207         """
00208         if type(value) is StringType:
00209             value = (value,)
00210         elif not value:
00211             # mostly harmless?
00212             value = ()
00213         f = self.getField('eventType')
00214         f.set(self, value, **kw) # set is ok
00215         if not alreadySet:
00216             self.setSubject(value, alreadySet=True, **kw)
00217 
00218     security.declareProtected(ModifyPortalContent, 'setSubject')
00219     def setSubject(self, value, alreadySet=False, **kw):
00220         """CMF compatibility method
00221 
00222         Changing the subject changes also the event type.
00223         """
00224         f = self.getField('subject')
00225         f.set(self, value, **kw) # set is ok
00226 
00227         # set the event type to the first subject
00228         if type(value) is StringType:
00229             v = (value, )
00230         elif value:
00231             v = value
00232         else:
00233             v = ()
00234 
00235         if not alreadySet:
00236             self.setEventType(v, alreadySet=True, **kw)
00237 
00238     security.declareProtected(View, 'getEventTypes')
00239     def getEventTypes(self):
00240         """fetch a list of the available event types from the vocabulary
00241         """
00242         f = self.getField('eventType')
00243         result = self.collectKeywords('eventType', f.accessor)
00244         return tuple(result)
00245 
00246     security.declarePrivate('cmf_edit')
00247     def cmf_edit(self, title=None, description=None, eventType=None,
00248              effectiveDay=None, effectiveMo=None, effectiveYear=None,
00249              expirationDay=None, expirationMo=None, expirationYear=None,
00250              start_date=None, start_time=None, startAMPM=None,
00251              end_date=None, stop_time=None, stopAMPM=None,
00252              location=None,
00253              contact_name=None, contact_email=None, contact_phone=None,
00254              event_url=None):
00255 
00256         if effectiveDay and effectiveMo and effectiveYear and start_time:
00257             sdate = '%s-%s-%s %s %s' % (effectiveDay, effectiveMo, effectiveYear,
00258                                          start_time, startAMPM)
00259         elif start_date:
00260             if not start_time:
00261                 start_time = '00:00:00'
00262             sdate = '%s %s' % (start_date, start_time)
00263         else:
00264             sdate = None
00265 
00266         if expirationDay and expirationMo and expirationYear and stop_time:
00267             edate = '%s-%s-%s %s %s' % (expirationDay, expirationMo,
00268                                         expirationYear, stop_time, stopAMPM)
00269         elif end_date:
00270             if not stop_time:
00271                 stop_time = '00:00:00'
00272             edate = '%s %s' % (end_date, stop_time)
00273         else:
00274             edate = None
00275 
00276         if sdate and edate:
00277             if edate < sdate:
00278                 edate = sdate
00279             self.setStartDate(sdate)
00280             self.setEndDate(edate)
00281 
00282         self.update(title=title, description=description, eventType=eventType,
00283                     location=location, contactName=contact_name,
00284                     contactEmail=contact_email, contactPhone=contact_phone,
00285                     eventUrl=event_url)
00286 
00287     security.declareProtected(View, 'post_validate')
00288     def post_validate(self, REQUEST=None, errors=None):
00289         """Validates start and end date
00290 
00291         End date must be after start date
00292         """
00293         if 'startDate' in errors or 'endDate' in errors:
00294             # No point in validating bad input
00295             return
00296         
00297         rstartDate = REQUEST.get('startDate', None)
00298         rendDate = REQUEST.get('endDate', None)
00299 
00300         if rendDate:
00301             try:
00302                 end = DateTime(rendDate)
00303             except:
00304                 errors['endDate'] = _(u'error_invalid_end_date',
00305                                       default=u'End date is not valid.')
00306         else:
00307             end = self.end()
00308         if rstartDate:
00309             try:
00310                 start = DateTime(rstartDate)
00311             except:
00312                 errors['startDate'] = _(u'error_invalid_start_date',
00313                                         default=u'Start date is not valid.')
00314         else:
00315             start = self.start()
00316 
00317         if 'startDate' in errors or 'endDate' in errors:
00318             # No point in validating bad input
00319             return
00320         
00321         if start > end:
00322             errors['endDate'] = _(u'error_end_must_be_after_start_date',
00323                                   default=u'End date must be after start date.')
00324 
00325     def _start_date(self):
00326         value = self['startDate']
00327         if value is None:
00328             value = self['creation_date']
00329         return DT2dt(value)
00330 
00331     security.declareProtected(View, 'start_date')
00332     start_date = ComputedAttribute(_start_date)
00333 
00334     def _end_date(self):
00335         value = self['endDate']
00336         if value is None:
00337             return self.start_date
00338         return DT2dt(value)
00339 
00340     security.declareProtected(View, 'end_date')
00341     end_date = ComputedAttribute(_end_date)
00342 
00343     def _duration(self):
00344         return self.end_date - self.start_date
00345 
00346     security.declareProtected(View, 'duration')
00347     duration = ComputedAttribute(_duration)
00348 
00349     def __cmp__(self, other):
00350         """Compare method
00351 
00352         If other is based on ATEvent, compare start, duration and title.
00353         #If other is a number, compare duration and number
00354         If other is a DateTime instance, compare start date with date
00355         In all other cases there is no specific order
00356         """
00357         # Please note that we can not use self.Title() here: the generated
00358         # edit accessor uses getToolByName, which ends up in
00359         # five.localsitemanager looking for a parent using a comparison
00360         # on this object -> infinite recursion.
00361         if IATEvent.isImplementedBy(other):
00362             return cmp((self.start_date, self.duration, self.title),
00363                        (other.start_date, other.duration, other.title))
00364         #elif isinstance(other, (int, long, float)):
00365         #    return cmp(self.duration, other)
00366         elif isinstance(other, DateTime):
00367             return cmp(self.start(), other)
00368         else:
00369             # TODO come up with a nice cmp for types
00370             return cmp(self.title, other)
00371 
00372     def __hash__(self):
00373         return hash((self.start_date, self.duration, self.title))
00374 
00375     security.declareProtected(ModifyPortalContent, 'update')
00376     def update(self, event=None, **kwargs):
00377         # Clashes with BaseObject.update, so
00378         # we handle gracefully
00379         info = {}
00380         if event is not None:
00381             for field in event.Schema().fields():
00382                 info[field.getName()] = event[field.getName()]
00383         elif kwargs:
00384             info = kwargs
00385         ATCTContent.update(self, **info)
00386 
00387 registerATCT(ATEvent, PROJECTNAME)