def toLocalizedTime(self, time=None, long_format=None, time_only=None, event=None, startend="start"): if not event: return self.context.restrictedTraverse("@@plone").toLocalizedTime( time, long_format, time_only) if ICatalogBrain.providedBy(event): event = event.getObject() if not IEvent.providedBy(event): return self.context.restrictedTraverse("@@plone").toLocalizedTime( time, long_format, time_only) rs = RecurrenceSupport(event) occurences = [ occ for occ in rs.occurrences(datetime.today()) if IEvent.providedBy(occ) ] if len(occurences) >= 1: # do not get object which started in the past if startend == "start": time = getattr(occurences[0], "start") elif startend == "end": time = getattr(occurences[-1], "end") return self.context.restrictedTraverse("@@plone").toLocalizedTime( time, long_format, time_only)
def EventTypes(context): """ Vocabulary for available event types. Insane stuff: All types are created temporary and checked if the provide the IEvent interface. At least, this function is cached forever the Zope process lives. """ # TODO: I'd love to query the factory for types, who's instances are # implementing a specific interface via the portal_factory API. portal = getSite() tmp_folder_id = 'event_types_temp_folder__%s' % random.randint(0, 99999999) portal.invokeFactory('Folder', tmp_folder_id) try: tmp_folder = portal._getOb(tmp_folder_id) portal_types = getToolByName(portal, 'portal_types') all_types = portal_types.listTypeInfo(portal) event_types = [] cnt = 0 for fti in all_types: if not getattr(fti, 'global_allow', False): continue cnt += 1 tmp_id = 'temporary__event_types__%s' % cnt tmp_obj = None fti.constructInstance(tmp_folder, tmp_id) tmp_obj = tmp_folder._getOb(tmp_id) if tmp_obj: if IEvent.providedBy(tmp_obj): event_types.append(fti.id) finally: # Delete the tmp_folder again tmp_folder.__parent__.manage_delObjects([tmp_folder_id]) return SimpleVocabulary.fromValues(event_types)
def test_fix_interface_for_event(self): self.obj.portal_type = 'Event' self.catalog.reindexObject(self.obj) self.portal.restrictedTraverse('fix_base_classes')() self.assertTrue(IEvent.providedBy(self.obj))
def timezone_handler(obj, event): """ When setting the startDate and endDate, the value of the timezone field isn't known, so we have to convert those timezone-naive dates into timezone-aware ones afterwards. """ if not IEvent.providedBy(obj): # don't run me, if i'm not installed return timezone = obj.getField('timezone').get(obj) start_field = obj.getField('startDate') end_field = obj.getField('endDate') start = start_field.get(obj) end = end_field.get(obj) def make_DT(value, timezone): return DateTime( value.year(), value.month(), value.day(), value.hour(), value.minute(), value.second(), timezone) start = make_DT(start, timezone).toZone('UTC') end = make_DT(end, timezone).toZone('UTC') start_field.set(obj, start) end_field.set(obj, end) obj.reindexObject()
def ticket_title_generator(obj): """Generate a title for the ticket, also using event information. """ event = obj ret = { 'title': obj.title, 'eventtitle': '', 'eventstart': '', 'eventend': '' } if ITicketOccurrence.providedBy(event): event = aq_parent(aq_parent(event)) # Traverse to the Occurrence object if IATEvent.providedBy(event): # get the request out of thin air to be able to publishTraverse to # the transient Occurrence object. traverser = OccTravAT(event, getRequest()) elif IDXEvent.providedBy(event): # TODO traverser = OccTravDX(event, getRequest()) else: raise NotImplementedError( u"There is no event occurrence traverser implementation for " u"this kind of object." ) try: event = traverser.publishTraverse(getRequest(), obj.id) except KeyError: # Maybe the ticket occurrence isn't valid anymore because the # event occurence doesn't exist anymore. # Just ignore that case. return ret elif ITicket.providedBy(event): event = aq_parent(event) if IEvent.providedBy(event) or IOccurrence.providedBy(event): acc = IEventAccessor(event) lstart = ulocalized_time( DT(acc.start), long_format=True, context=event ) lend = ulocalized_time( DT(acc.start), long_format=True, context=event ) # XXX: no unicode, store as utf-8 encoded string instead ret = dict( title=u'%s - %s (%s - %s)' % ( safe_unicode(acc.title), safe_unicode(obj.title), lstart, lend, ), eventtitle=acc.title, eventstart=acc.start, eventend=acc.end, ) return ret
def set_allDay(self, v): v = bool(v) if HAS_PAE: if IEvent.providedBy(self.context): acc = IEventAccessor(self.context) acc.whole_day = v return self._all_day = v
def _allDay(self): if HAS_PAE: if IEvent.providedBy(self.context): acc = IEventAccessor(self.context) return acc.whole_day or False if self._all_day is not None: return bool(self._all_day) return False
def test_factory(self): fti = queryUtility( IDexterityFTI, name='Event' ) factory = fti.factory new_object = createObject(factory) self.assertTrue(IEvent.providedBy(new_object))
def test_upgrade_step_1(self): _dummy_1 = mkDummyInContext(MockATEvent_1, oid='dummy_1', context=self.portal, schema=ATContentTypeSchema) _dummy_1.title = 'Foo' _dummy_1.reindexObject() event = self.portal['dummy_1'] self.assertTrue(not IEvent.providedBy(event)) del event upgrade_step_1(self.portal) event = self.portal['dummy_1'] self.assertTrue(IEvent.providedBy(event)) self.assertEqual('', event.recurrence) self.assertEqual('Foo', event.Title())
def data_postprocessing(obj, event): """When setting the startDate and endDate, the value of the timezone field isn't known, so we have to convert those timezone-naive dates into timezone-aware ones afterwards. For whole day events, set start time to 0:00:00 and end time toZone 23:59:59. """ if not IEvent.providedBy(obj): # don't run me, if i'm not installed return timezone = obj.getField('timezone').get(obj) start_field = obj.getField('startDate') end_field = obj.getField('endDate') # The previous_timezone is set, when the timezone has changed to another # value. In this case we need to convert the UTC dt values to the # previous_timezone, so that we get the datetime values, as the user # entered them. However, this value might be always set, even when creating # an event, since ObjectModifiedEvent is called several times when editing. prev_tz = getattr(obj, 'previous_timezone', None) if prev_tz: delattr(obj, 'previous_timezone') def _fix_zone(dt, tz): if not dt.timezoneNaive(): # The object is edited and the value alreadty stored in UTC on the # object. In this case we want the value converted to the given # timezone, in which the user entered the data. dt = dt.toZone(tz) return dt start = _fix_zone(start_field.get(obj), prev_tz and prev_tz or timezone) end = _fix_zone(end_field.get(obj), prev_tz and prev_tz or timezone) def make_DT(value, timezone): return DateTime( value.year(), value.month(), value.day(), value.hour(), value.minute(), value.second(), timezone) start = make_DT(start, timezone) end = make_DT(end, timezone) if obj.getWholeDay(): start = DateTime('%s 0:00:00 %s' % (start.Date(), timezone)) end = DateTime('%s 23:59:59 %s' % (end.Date(), timezone)) start_field.set(obj, start.toZone('UTC')) end_field.set(obj, end.toZone('UTC')) obj.reindexObject()
def ticket_title_generator(obj): """Generate a title for the ticket, also using event information. """ event = obj ret = { 'title': obj.title, 'eventtitle': '', 'eventstart': '', 'eventend': '' } if ITicketOccurrence.providedBy(event): event = aq_parent(aq_parent(event)) # Traverse to the Occurrence object if IATEvent.providedBy(event): # get the request out of thin air to be able to publishTraverse to # the transient Occurrence object. traverser = OccTravAT(event, getRequest()) elif IDXEvent.providedBy(event): # TODO traverser = OccTravDX(event, getRequest()) else: raise NotImplementedError( u"There is no event occurrence traverser implementation for " u"this kind of object.") try: event = traverser.publishTraverse(getRequest(), obj.id) except KeyError: # Maybe the ticket occurrence isn't valid anymore because the # event occurence doesn't exist anymore. # Just ignore that case. return ret elif ITicket.providedBy(event): event = aq_parent(event) if IEvent.providedBy(event) or IOccurrence.providedBy(event): acc = IEventAccessor(event) lstart = ulocalized_time(DT(acc.start), long_format=True, context=event) lend = ulocalized_time(DT(acc.start), long_format=True, context=event) # XXX: no unicode, store as utf-8 encoded string instead ret = dict( title=u'%s - %s (%s - %s)' % ( safe_unicode(acc.title), safe_unicode(obj.title), lstart, lend, ), eventtitle=acc.title, eventstart=acc.start, eventend=acc.end, ) return ret
def test_recurrence_occurrences(self): result = IRecurrenceSupport(self.data).occurrences() self.assertEqual(4, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def _get_event_url(ticket): """Return the URL to an event or event occurrence for a ticket or ticket occurrence. """ url = ticket.absolute_url() if ITicketOccurrence.providedBy(ticket): # assumption1: ticketoccurrence id = occurrence id # assumption2: ticketoccurrence parent = ticket event = aq_parent(aq_parent(ticket)) if IEvent.providedBy(event): url = '{}/{}'.format(event.absolute_url(), ticket.id) elif ITicket.providedBy(ticket): event = aq_parent(ticket) if IEvent.providedBy(event): url = event.absolute_url() return url
def test_implementsInterfaces(self): """Test if an ATEvent object implements all relevant interfaces. """ self.assertTrue(IEvent.providedBy(self.obj)) self.assertTrue(IEventRecurrence.providedBy(self.obj)) self.assertTrue(IATEvent.providedBy(self.obj)) self.assertTrue(IATEventRecurrence.providedBy(self.obj)) self.assertTrue(IATEvent_ATCT.providedBy(self.obj)) self.assertTrue(verifyObject(IATEvent_ATCT, self.obj))
def _get_event_url(ticket): """Return the URL to an event or event occurrence for a ticket or ticket occurrence. """ url = ticket.absolute_url() if ITicketOccurrence.providedBy(ticket): # assumption1: ticketoccurrence id = occurrence id # assumption2: ticketoccurrence parent = ticket event = aq_parent(aq_parent(ticket)) if IEvent.providedBy(event): url = "{}/{}".format(event.absolute_url(), ticket.id) elif ITicket.providedBy(ticket): event = aq_parent(ticket) if IEvent.providedBy(event): url = event.absolute_url() return url
def expand_events(events, ret_mode, start=None, end=None, sort=None, sort_reverse=None): """Expand to the recurrence occurrences of a given set of events. :param events: IEvent based objects or IEventAccessor object wrapper. :param ret_mode: Return type of search results. These options are available: * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. Option "1" (brains) is not supported. :type ret_mode: integer [2|3] :param start: Date, from which on events should be expanded. :type start: Python datetime. :param end: Date, until which events should be expanded. :type end: Python datetime :param sort: Object or IEventAccessor Attribute to sort on. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean """ assert (ret_mode is not RET_MODE_BRAINS) exp_result = [] for it in events: obj = it.getObject() if getattr(it, 'getObject', False) else it if IEventRecurrence.providedBy(obj): occurrences = [ _obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end) ] elif IEvent.providedBy(obj): occurrences = [_obj_or_acc(obj, ret_mode)] else: # No IEvent based object. Could come from a collection. continue exp_result += occurrences if sort: exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() return exp_result
def test_recurrence_occurrences_with_range_start_1(self): # Test with range rs = datetime.datetime(2011, 11, 15, 11, 0, tzinfo=self.tz) result = IRecurrenceSupport(self.data).occurrences(range_start=rs) self.assertEqual(4, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def filterOccurrenceEvents(self, events): filter_events = [] for event in events: if not IEvent.providedBy(event): ocurrence = event event = event.aq_parent if event not in filter_events: event.ocstart = ocurrence.start event.ocend = ocurrence.end filter_events.append(event) else: filter_events.append(event) return filter_events
def expand_events(events, ret_mode, start=None, end=None, sort=None, sort_reverse=None): """Expand to the recurrence occurrences of a given set of events. :param events: IEvent based objects or IEventAccessor object wrapper. :param ret_mode: Return type of search results. These options are available: * 2 (objects): Return results as IEvent and/or IOccurrence objects. * 3 (accessors): Return results as IEventAccessor wrapper objects. Option "1" (brains) is not supported. :type ret_mode: integer [2|3] :param start: Date, from which on events should be expanded. :type start: Python datetime. :param end: Date, until which events should be expanded. :type end: Python datetime :param sort: Object or IEventAccessor Attribute to sort on. :type sort: string :param sort_reverse: Change the order of the sorting. :type sort_reverse: boolean """ assert(ret_mode is not RET_MODE_BRAINS) exp_result = [] for it in events: obj = it.getObject() if getattr(it, 'getObject', False) else it if IEventRecurrence.providedBy(obj): occurrences = [_obj_or_acc(occ, ret_mode) for occ in IRecurrenceSupport(obj).occurrences(start, end)] elif IEvent.providedBy(obj): occurrences = [_obj_or_acc(obj, ret_mode)] else: # No IEvent based object. Could come from a collection. continue exp_result += occurrences if sort: exp_result.sort(key=lambda x: _get_compare_attr(x, sort)) if sort_reverse: exp_result.reverse() return exp_result
def test_recurrence_occurrences_with_range_start_and_end(self): # Test with range rs = datetime.datetime(2011, 11, 11, 11, 0, tzinfo=self.tz) re = datetime.datetime(2011, 11, 12, 11, 0, tzinfo=self.tz) result = IRecurrenceSupport(self.data).occurrences(range_start=rs, range_end=re) result = list(result) # cast generator to list self.assertEqual(2, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def test_recurrence(self): tz = pytz.timezone('Europe/Vienna') duration = datetime.timedelta(days=4) data = MockEvent() data.start = datetime.datetime(2011, 11, 11, 11, 00, tzinfo=tz) data.end = data.start + duration data.recurrence = 'RRULE:FREQ=DAILY;COUNT=4' zope.interface.alsoProvides(data, IEvent, IEventRecurrence) result = IRecurrenceSupport(data).occurrences() self.assertEqual(4, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def whole_day_handler(obj, event): """ For whole day events only, set start time to 0:00:00 and end time to 23:59:59 """ if not IEvent.providedBy(obj): # don't run me, if i'm not installed return if not obj.whole_day: return startDate = obj.startDate.toZone(obj.timezone) startDate = startDate.Date() + ' 0:00:00 ' + startDate.timezone() endDate = obj.endDate.toZone(obj.timezone) endDate = endDate.Date() + ' 23:59:59 ' + endDate.timezone() obj.setStartDate(DateTime(startDate)) # TODO: setting needed? aren't above operations operating on the instances itself? obj.setEndDate(DateTime(endDate)) obj.reindexObject() # reindex obj to store upd values in catalog
def toLocalizedTime(self, time=None, long_format=None, time_only=None, event=None, startend='start'): if event: if not IEvent.providedBy(event): event = event.getObject() rs = RecurrenceSupport(event) occurences = [occ for occ in rs.occurrences(datetime.today())] if len(occurences) >= 1: # do not get object which started in the past if startend == 'start': time = getattr(occurences[0], 'start') elif startend == 'end': time = getattr(occurences[-1], 'end') return self.context.restrictedTraverse('@@plone').toLocalizedTime( time, long_format, time_only)
def test_recurrence(self): tz = pytz.timezone('Europe/Vienna') duration = timedelta(days=4) mock = MockEvent() mock.start = tz.localize(datetime(2011, 11, 11, 11, 0)) mock.end = mock.start + duration mock.recurrence = 'RRULE:FREQ=DAILY;COUNT=4' zope.interface.alsoProvides(mock, IEvent, IEventBasic, IEventRecurrence, IDXEvent, IDXEventRecurrence) result = IRecurrenceSupport(mock).occurrences() result = list(result) # cast generator to list self.assertEqual(4, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def test_recurrence(self): tz = pytz.timezone('Europe/Vienna') duration = timedelta(days=4) mock = MockEvent() mock.start = tz.localize(datetime(2011, 11, 11, 11, 0)) mock.end = mock.start + duration mock.recurrence = 'RRULE:FREQ=DAILY;COUNT=4' zope.interface.alsoProvides( mock, IEvent, IEventBasic, IEventRecurrence, IDXEvent, IDXEventRecurrence) result = IRecurrenceSupport(mock).occurrences() result = list(result) # cast generator to list self.assertEqual(4, len(result)) # First occurrence is an IEvent object self.assertTrue(IEvent.providedBy(result[0])) # Subsequent ones are IOccurrence objects self.assertTrue(IOccurrence.providedBy(result[1]))
def events(self, ret_mode=3, batch=True): res = [] is_col = self.is_collection is_top = self.is_topic if is_col or is_top: ctx = self.default_context if is_col: res = ctx.results(batch=False, sort_on='start', brains=False) else: res = ctx.queryCatalog( REQUEST=self.request, batch=False, full_objects=True ) # TODO: uff, we have to walk through all results... if ret_mode == 3: res = [IEventAccessor(obj) for obj in res if IEvent.providedBy(obj)] else: res = self._get_events(ret_mode) if batch: b_start = self.b_start b_size = self.b_size res = Batch(res, size=b_size, start=b_start, orphan=self.orphan) return res
def dict_from_item(item): if hasPloneAppEvent and (IEvent.providedBy(item) or IOccurrence.providedBy(item)): is_occurrence = IOccurrence.providedBy(item) and True or False acc = IEventAccessor(item) return { "status": "ok", "id": "UID_%s" % (acc.uid), "title": acc.title, "description": acc.description, "start": acc.start.isoformat(), "end": acc.end.isoformat(), "url": acc.url, "editable": editable, "allDay": acc.whole_day, "className": "contextualContentMenuEnabled %s %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or "", is_occurrence and "occurrence" or "", ), "color": color, } elif IATEvent.providedBy(item): allday = (item.end() - item.start()) > 1.0 adapted = interfaces.ISFBaseEventFields(item, None) if adapted: allday = adapted.allDay return [ { "status": "ok", "id": "UID_%s" % (item.UID()), "title": item.Title(), "description": item.Description(), "start": item.start().rfc822(), "end": item.end().rfc822(), "url": item.absolute_url(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % (state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color, } ] elif ICatalogBrain.providedBy(item): if type(item.end) != DateTime: brainend = DateTime(item.end) brainstart = DateTime(item.start) else: brainend = item.end brainstart = item.start allday = (brainend - brainstart) > 1.0 if getattr(item, "SFAllDay", None) in [False, True]: allday = item.SFAllDay return [ { "status": "ok", "id": "UID_%s" % (item.UID), "title": item.Title, "description": item.Description, "start": brainstart.rfc822(), "end": brainend.rfc822(), "url": item.getURL(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % (state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color, } ] else: raise ValueError("item type not supported for: %s" % repr(item))
def dict_from_item(item): if hasPloneAppEvent and (IEvent.providedBy(item) or IOccurrence.providedBy(item)): # plone.app.event DX or AT Event is_occurrence = IOccurrence.providedBy(item) acc = IEventAccessor(item) return { "status": "ok", "id": "UID_%s" % (acc.uid), "title": acc.title, "description": acc.description, "start": acc.start.isoformat(), "end": acc.end.isoformat(), "url": acc.url, "editable": editable, "allDay": acc.whole_day, "className": "contextualContentMenuEnabled %s %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or "", is_occurrence and "occurrence" or ""), "color": color} elif IATEvent.providedBy(item): # Products.ATContentTypes ATEvent allday = (item.end() - item.start()) > 1.0 adapted = interfaces.ISFBaseEventFields(item, None) if adapted: allday = adapted.allDay return { "status": "ok", "id": "UID_%s" % (item.UID()), "title": item.Title(), "description": item.Description(), "start": item.start().ISO8601(), "end": item.end().ISO8601(), "url": item.absolute_url(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color} elif ICatalogBrain.providedBy(item): # Event catalog brain if type(item.end) != DateTime: brainend = DateTime(item.end) brainstart = DateTime(item.start) else: brainend = item.end brainstart = item.start allday = (brainend - brainstart) > 1.0 if getattr(item, 'SFAllDay', None) in [False, True]: allday = item.SFAllDay # Set Mexico City Time brainstart = DateTime(' '.join( (brainstart.Date(), brainstart.Time(), 'America/Mexico_City'))) brainend = DateTime(' '.join( (brainend.Date(), brainend.Time(), 'America/Mexico_City'))) return { "status": "ok", "id": "UID_%s" % (item.UID), "title": item.Title, "description": item.Description, "start": brainstart.ISO8601(), "end": brainend.ISO8601(), "url": item.getURL(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color} else: raise ValueError('item type not supported for: %s' % repr(item))
def isEvent(self, item): if getattr(item, 'getObject', False): item = item.getObject() return IEvent.providedBy(item)
def construct_icalendar(context, events): """Returns an icalendar.Calendar object. :param context: A content object, which is used for calendar details like Title and Description. Usually a container, collection or the event itself. :param events: The list of event objects, which are included in this calendar. """ cal = icalendar.Calendar() cal.add('prodid', PRODID) cal.add('version', VERSION) cal_tz = default_timezone(context) if cal_tz: cal.add('x-wr-timezone', cal_tz) tzmap = {} if not getattr(events, '__getitem__', False): events = [events] for event in events: if ICatalogBrain.providedBy(event) or\ IContentListingObject.providedBy(event): event = event.getObject() if not (IEvent.providedBy(event) or IOccurrence.providedBy(event)): # Must be an event. continue acc = IEventAccessor(event) tz = acc.timezone # TODO: the standard wants each recurrence to have a valid timezone # definition. sounds decent, but not realizable. if not acc.whole_day: # whole day events are exported as dates without # timezone information if isinstance(tz, tuple): tz_start, tz_end = tz else: tz_start = tz_end = tz tzmap = add_to_zones_map(tzmap, tz_start, acc.start) tzmap = add_to_zones_map(tzmap, tz_end, acc.end) cal.add_component(IICalendarEventComponent(event).to_ical()) for (tzid, transitions) in tzmap.items(): cal_tz = icalendar.Timezone() cal_tz.add('tzid', tzid) cal_tz.add('x-lic-location', tzid) for (transition, tzinfo) in transitions.items(): if tzinfo['dst']: cal_tz_sub = icalendar.TimezoneDaylight() else: cal_tz_sub = icalendar.TimezoneStandard() cal_tz_sub.add('tzname', tzinfo['name']) cal_tz_sub.add('dtstart', transition) cal_tz_sub.add('tzoffsetfrom', tzinfo['tzoffsetfrom']) cal_tz_sub.add('tzoffsetto', tzinfo['tzoffsetto']) # TODO: add rrule # tzi.add('rrule', # {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'}) cal_tz.add_component(cal_tz_sub) cal.add_component(cal_tz) return cal
def test_get_events(self): # whole range res = get_events(self.portal) self.assertEqual(len(res), 4) res = get_events(self.portal, start=self.past, end=self.future) self.assertEqual(len(res), 4) res = get_events(self.portal, end=self.future) self.assertEqual(len(res), 4) res = get_events(self.portal, start=self.past) self.assertEqual(len(res), 4) # Limit res = get_events(self.portal, limit=2) self.assertEqual(len(res), 2) # Return objects res = get_events(self.portal, ret_mode=RET_MODE_OBJECTS) self.assertTrue(IEvent.providedBy(res[0])) # Return IEventAccessor res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS) self.assertTrue(IEventAccessor.providedBy(res[0])) # Test sorting self.assertTrue(res[0].start < res[-1].start) # Test reversed sorting res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort_reverse=True) self.assertTrue(res[0].start > res[-1].start) # Test sort_on res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort="start") self.assertEqual( [it.title for it in res][2:], [u'Now Event', u'Future Event'] ) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, sort="end") self.assertEqual( [it.title for it in res], [u'Past Event', u'Now Event', u'Future Event', u'Long Event'] ) # Test expansion res = get_events(self.portal, ret_mode=RET_MODE_OBJECTS, expand=True) self.assertEqual(len(res), 8) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True) self.assertEqual(len(res), 8) # Test sorting self.assertTrue(res[0].start < res[-1].start) res = get_events(self.portal, ret_mode=RET_MODE_ACCESSORS, expand=True, sort_reverse=True) # Test sorting self.assertTrue(res[0].start > res[-1].start) # only on now-date res = get_events(self.portal, start=self.now, end=self.now) self.assertEqual(len(res), 2) # only on now-date as date # NOTE: converting self.now to python datetime to allow testing also # with dates as Zope DateTime objects. res = get_events(self.portal, start=pydt(self.now).date(), end=pydt(self.now).date()) self.assertEqual(len(res), 2) # only on past date res = get_events(self.portal, start=self.past, end=self.past) self.assertEqual(len(res), 2) # one recurrence occurrence in far future res = get_events(self.portal, start=self.far, end=self.far) self.assertEqual(len(res), 1) # from now on res = get_events(self.portal, start=self.now) self.assertEqual(len(res), 3) # until now res = get_events(self.portal, end=self.now) self.assertEqual(len(res), 3) # in subfolder path = '/'.join(self.portal.sub.getPhysicalPath()) res = get_events(self.portal, path=path) self.assertEqual(len(res), 1)
def is_event_collection(self, brains): if len(brains) > 0: obj = brains[0].getObject() return IEvent.providedBy(obj) return False
def test_adding(self): self.portal.invokeFactory( 'Event', 'doc1' ) self.assertTrue(IEvent.providedBy(self.portal['doc1']))
def map_link(self): if IEvent.providedBy(self.context): return getattr(self.context, 'map_link', None)
def dict_from_item(item): if hasPloneAppEvent and (IEvent.providedBy(item) or IOccurrence.providedBy(item)): # plone.app.event DX or AT Event is_occurrence = IOccurrence.providedBy(item) acc = IEventAccessor(item) return { "status": "ok", "id": "UID_%s" % (acc.uid), "title": acc.title, "description": acc.description, "start": acc.start.isoformat(), "end": acc.end.isoformat(), "url": acc.url, "editable": editable, "allDay": acc.whole_day, "className": "contextualContentMenuEnabled %s %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or "", is_occurrence and "occurrence" or ""), "color": color} elif IATEvent.providedBy(item): # Products.ATContentTypes ATEvent allday = (item.end() - item.start()) > 1.0 adapted = interfaces.ISFBaseEventFields(item, None) if adapted: allday = adapted.allDay return { "status": "ok", "id": "UID_%s" % (item.UID()), "title": item.Title(), "description": item.Description(), "start": item.start().ISO8601(), "end": item.end().ISO8601(), "url": item.absolute_url(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color} elif ICatalogBrain.providedBy(item): # Event catalog brain if type(item.end) != DateTime: brainend = DateTime(item.end) brainstart = DateTime(item.start) else: brainend = item.end brainstart = item.start allday = (brainend - brainstart) > 1.0 if getattr(item, 'SFAllDay', None) in [False, True]: allday = item.SFAllDay return { "status": "ok", "id": "UID_%s" % (item.UID), "title": item.Title, "description": item.Description, "start": brainstart.ISO8601(), "end": brainend.ISO8601(), "url": item.getURL(), "editable": editable, "allDay": allday, "className": "contextualContentMenuEnabled %s %s %s" % ( state and "state-%s" % str(state) or "", editable and "editable" or "", css and css or ""), "color": color} else: raise ValueError('item type not supported for: %s' % repr(item))
def data(self): context = self.context data = {} if ICollegeHomepage.providedBy(self.context): data = { '@context': 'http://schema.org', '@type': 'EducationalOrganization', 'address': { '@type': 'PostalAddress', 'addressLocality': 'University Park', 'addressRegion': 'PA', 'postalCode': '16802', 'streetAddress': 'Penn State University'}, 'logo': 'https://agsci.psu.edu/psu-agsciences-logo.png', 'name': 'Penn State College of Agricultural Sciences', 'sameAs': [ 'https://www.facebook.com/agsciences', 'https://www.twitter.com/agsciences', 'https://plus.google.com/+PennStateAgSciences', 'https://instagram.com/agsciences', 'https://www.linkedin.com/company/penn-state-college-of-agricultural-sciences', 'https://www.youtube.com/psuagsciences', 'https://en.wikipedia.org/wiki/Penn_State_College_of_Agricultural_Sciences'], 'telephone': '+1-814-865-7521', 'url': 'https://agsci.psu.edu' } elif IEvent.providedBy(context): data = { '@context': 'http://schema.org', '@type': 'Event', 'name': context.Title(), 'description' : context.Description(), 'startDate' : localize(context.start).isoformat(), 'endDate' : localize(context.end).isoformat(), 'url' : context.absolute_url(), 'location' : { "@type" : "Place", "address" : getattr(context, 'location', ''), "name" : getattr(context, 'location', ''), } } elif INewsItem.providedBy(context): data = { '@context': 'http://schema.org', '@type': 'Article', 'headline': context.Title(), 'description' : context.Description(), 'datePublished' : localize(context.effective()).isoformat(), 'url' : context.absolute_url(), } elif IPerson.providedBy(context): # Job Title job_titles = getattr(context, 'job_titles', []) if job_titles: job_title = job_titles[0] else: job_title = "" # Email email = getattr(context, 'email', '') # Name (first_name, middle_name, last_name) = [getattr(context, x, '') for x in ('first_name', 'middle_name', 'last_name')] # Address street_address = getattr(context, 'street_address', []) if street_address: street_address = [x for x in street_address if x] street_address = ', '.join(street_address) city = getattr(context, 'city', '') state = getattr(context, 'state', '') zip_code = getattr(context, 'zip_code', '') # Phone phone_number = getattr(context, 'phone_number', '') data = { '@context': 'http://schema.org', '@type': 'Person', 'url' : context.absolute_url(), 'email' : email, 'givenName' : first_name, 'additionalName' : middle_name, 'familyName' : last_name, 'telephone' : phone_number, 'jobTitle' : job_title, 'workLocation' : { '@type' : 'PostalAddress', 'addressCountry' : 'US', 'addressLocality' : city, 'addressRegion' : state, 'postalCode' : zip_code, 'streetAddress' : street_address, } } data['image'] = self.image if data: return json.dumps(data, indent=4)
def test_upgrade_step_1(self): upgrade_step_1(self.portal) event = self.portal["dummy_1"] self.assertTrue(IEvent.providedBy(event)) self.assertEqual("", event.recurrence) self.assertEqual("Foo", event.Title())
def data_postprocessing(obj, event): """When setting the startDate and endDate, the value of the timezone field isn't known, so we have to convert those timezone-naive dates into timezone-aware ones afterwards. For whole day events, set start time to 0:00:00 and end time to 23:59:59. For open end events, set end time to 23:59:59. """ if not IEvent.providedBy(obj): # don't run me, if i'm not installed return timezone = obj.getField('timezone').get(obj) start_field = obj.getField('startDate') end_field = obj.getField('endDate') # The previous_timezone is set, when the timezone has changed to another # value. In this case we need to convert the UTC dt values to the # previous_timezone, so that we get the datetime values, as the user # entered them. However, this value might be always set, even when creating # an event, since ObjectModifiedEvent is called several times when editing. prev_tz = getattr(obj, 'previous_timezone', None) if prev_tz: delattr(obj, 'previous_timezone') def _fix_zone(dt, tz): if not dt.timezoneNaive(): # The object is edited and the value alreadty stored in UTC on the # object. In this case we want the value converted to the given # timezone, in which the user entered the data. dt = dt.toZone(tz) return dt start = _fix_zone(start_field.get(obj), prev_tz and prev_tz or timezone) end = _fix_zone(end_field.get(obj), prev_tz and prev_tz or timezone) def make_DT(value, timezone): return DateTime( value.year(), value.month(), value.day(), value.hour(), value.minute(), int(value.second()), # No microseconds timezone) start = make_DT(start, timezone) end = make_DT(end, timezone) whole_day = obj.getWholeDay() open_end = obj.getOpenEnd() if whole_day: start = DateTime('%s 0:00:00 %s' % (start.Date(), timezone)) if open_end: end = start # Open end events end on same day if open_end or whole_day: end = DateTime('%s 23:59:59 %s' % (end.Date(), timezone)) start_field.set(obj, start.toZone('UTC')) end_field.set(obj, end.toZone('UTC')) if not obj.getEventUid(): # sync_uid has to be set for icalendar data exchange. uid = IUUID(obj) # We don't want to fail when getRequest() returns None, e.g when # creating an event during test layer setup time. request = getRequest() or {} domain = request.get('HTTP_HOST') obj.setEventUid('%s%s' % ( uid, domain and '@%s' % domain or '' )) obj.reindexObject()
def is_event(self, obj): if getattr(obj, 'getObject', False): obj = obj.getObject() return IEvent.providedBy(obj)
def data_postprocessing(obj, event): """When setting the startDate and endDate, the value of the timezone field isn't known, so we have to convert those timezone-naive dates into timezone-aware ones afterwards. For whole day events, set start time to 0:00:00 and end time to 23:59:59. For open end events, set end time to 23:59:59. """ if not IEvent.providedBy(obj): # don't run me, if i'm not installed return timezone = obj.getField('timezone').get(obj) start_field = obj.getField('startDate') end_field = obj.getField('endDate') # The previous_timezone is set, when the timezone has changed to another # value. In this case we need to convert the UTC dt values to the # previous_timezone, so that we get the datetime values, as the user # entered them. However, this value might be always set, even when creating # an event, since ObjectModifiedEvent is called several times when editing. prev_tz = getattr(obj, 'previous_timezone', None) if prev_tz: delattr(obj, 'previous_timezone') def _fix_zone(dt, tz): if not dt.timezoneNaive(): # The object is edited and the value alreadty stored in UTC on the # object. In this case we want the value converted to the given # timezone, in which the user entered the data. dt = dt.toZone(tz) return dt start = _fix_zone(start_field.get(obj), prev_tz and prev_tz or timezone) end = _fix_zone(end_field.get(obj), prev_tz and prev_tz or timezone) def make_DT(value, timezone): return DateTime( value.year(), value.month(), value.day(), value.hour(), value.minute(), int(value.second()), # No microseconds timezone) start = make_DT(start, timezone) end = make_DT(end, timezone) whole_day = obj.getWholeDay() open_end = obj.getOpenEnd() if whole_day: start = DateTime('%s 0:00:00 %s' % (start.Date(), timezone)) if open_end: end = start # Open end events end on same day if open_end or whole_day: end = DateTime('%s 23:59:59 %s' % (end.Date(), timezone)) start_field.set(obj, start.toZone('UTC')) end_field.set(obj, end.toZone('UTC')) if not obj.getEventUid(): # sync_uid has to be set for icalendar data exchange. uid = IUUID(obj) # We don't want to fail when getRequest() returns None, e.g when # creating an event during test layer setup time. request = getRequest() or {} domain = request.get('HTTP_HOST') obj.setEventUid('%s%s' % (uid, domain and '@%s' % domain or '')) obj.reindexObject()
def __call__(self, *args, **kwargs): if not self._loaded: self._load_state() recipient = kwargs.get('recipient', None) if not IMailRecipient.providedBy(recipient): recipient = self._recipient_from_request() message = MIMEMultipart() self._set_headers(message, recipient) if HAS_PAE and IEvent.providedBy(self.context): accessor = IEventAccessor(self.context) timezone = accessor.timezone if not timezone: timezone = pytz.UTC timezone = pytz.timezone(timezone) start = accessor.start.astimezone(timezone) # localtime of event else: start = self.context.start() data = { 'FROM_NAME' : self.sender.reply_name, 'FROM_EMAIL' : self.sender.reply_address, 'DATE_FORMATTED' : self.timefn( start, context=aq_inner(self.context), request=self.request, ), 'TIME_FORMATTED' : self.timefn( start, time_only=True, context=aq_inner(self.context), request=self.request, ), 'ITEM_TITLE' : self.context.Title(), 'ITEM_DESCRIPTION' : self.context.Description(), 'ITEM_URL' : self.context.absolute_url(), 'RSVP_URL' : self._rsvp_url(), 'ATTACH_FMT' : 'A vCal', } if HAS_PAE and IEvent.providedBy(self.context): data['ATTACH_FMT'] = 'An iCalendar (.ics)' data['TIME_FORMATTED'] += ' (%s / %s)' % ( start.tzinfo.tzname(start), utc_offset_label(start), ) body = INVITE_EMAIL_BODY % data message.attach(MIMEText(body)) if HAS_PAE and IEvent.providedBy(self.context): attachment = MIMEBase('text', 'calendar') attachment.set_payload(self._ical()) #data encoders.encode_base64(attachment) attachment.add_header('Content-Disposition', 'attachment', filename='event.ics') else: attachment = MIMEBase('text', 'x-vCalendar') attachment.set_payload(self._vcal()) #data encoders.encode_base64(attachment) attachment.add_header('Content-Disposition', 'attachment', filename='event.vcs') message.attach(attachment) return message.as_string()