plone.app.event.recurrence — plone.app.event 1.0dev documentation

### Navigation

- index
- modules |
- plone.app.event 1.0dev documentation »
- Module code »

# Source code for plone.app.event.recurrence

    from Acquisition import aq_parent
    from OFS.SimpleItem import SimpleItem
    from Products.CMFPlone.utils import safe_unicode
    from Products.Five.browser import BrowserView
    from plone.app.event.base import guess_date_from
    from plone.app.imaging.scaling import ImageScaling as ATImageScaling
    from plone.event.interfaces import IEventAccessor
    from plone.event.interfaces import IEventRecurrence
    from plone.event.interfaces import IOccurrence
    from plone.event.interfaces import IRecurrenceSupport
    from plone.event.recurrence import recurrence_sequence_ical
    from plone.event.utils import is_same_day
    from plone.namedfile.scaling import ImageScale as DXImageScaling
    from zope.component import adapts
    from zope.interface import Interface
    from zope.interface import implements
    from zope.publisher.interfaces.browser import IBrowserRequest
    import itertools

    try:
        from repoze.zope2.publishtraverse import DefaultPublishTraverse
    except ImportError:
        from ZPublisher.BaseRequest import DefaultPublishTraverse

    try:
        from plone.app.event.at.interfaces import IATEvent
    except ImportError:
        class IATEvent(Interface):
            pass
    try:
        from plone.app.event.dx.interfaces import IDXEvent
    except ImportError:
        class IDXEvent(Interface):
            pass

    class RecurrenceSupport(object):
        """IRecurrenceSupport Adapter.
        """
        implements(IRecurrenceSupport)
        adapts(IEventRecurrence)

        def __init__(self, context):
            self.context = context

        def occurrences(self, range_start=None, range_end=None):
            """Return all occurrences of an event, possibly within a start and end
            limit.

            Please note: Events beginning before range_start but ending afterwards
                         won't be found.

            TODO: test with event start = 21st feb, event end = start+36h,
            recurring for 10 days, range_start = 1st mar, range_end = last Mark
            """
            event = IEventAccessor(self.context)
            starts = recurrence_sequence_ical(event.start,
                                              recrule=event.recurrence,
                                              from_=range_start, until=range_end)

            if range_start and\
                    event.start < range_start and\
                    event.end >= range_start and\
                    event.start not in starts:
                # Include event, which started before range but lasts until it.
                starts = itertools.chain(starts, [event.start])

            # We get event ends by adding a duration to the start. This way, we
            # prevent that the start and end lists are of different size if an
            # event starts before range_start but ends afterwards.
            duration = event.duration

            # XXX potentially occurrence won't need to be wrapped anymore
            # but doing it for backwards compatibility as views/templates
            # still rely on acquisition-wrapped objects.
            def get_obj(start):
                if event.start.replace(microsecond=0) == start:
                    # If the occurrence date is the same as the event object, the
                    # occurrence is the event itself. return it as such.
                    # Dates from recurrence_sequence_ical are explicitly without
                    # microseconds, while event.start may contain it. So we have to
                    # remove it for a valid comparison.
                    return self.context
                return Occurrence(
                    id=str(start.date()),
                    start=start,
                    end=start + duration).__of__(self.context)

            events = map(get_obj, starts)
            return events

    class OccurrenceTraverser(DefaultPublishTraverse):
        """Generic Occurrence traverser.

        Please note: the .at and .dx subpackages implement their own Occurrence
        traversers.
        """
        adapts(IEventRecurrence, IBrowserRequest)

        def publishTraverse(self, request, name):
            context = self.context
            dateobj = guess_date_from(name, context)
            if dateobj:
                occurrence = IRecurrenceSupport(context).occurrences(
                    range_start=dateobj)[0]
                occ_acc = IEventAccessor(occurrence)
                if is_same_day(dateobj, occ_acc.start):
                    return occurrence
            return self.fallbackTraverse(request, name)

        def fallbackTraverse(self, request, name):
            return super(OccurrenceTraverser, self).publishTraverse(request, name)

    class Occurrence(SimpleItem):
        implements(IOccurrence)

        def __init__(self, id, start, end):
            self.id = id
            self.start = start
            self.end = end

    class EventOccurrenceAccessor(object):
        """Generic event accessor adapter implementation for Occurrence objects.
        """
        implements(IEventAccessor)
        adapts(IOccurrence)

        def __init__(self, context):
            object.__setattr__(self, 'context', context)

            own_attr = ['start', 'end', 'url', 'parent']
            object.__setattr__(self, '_own_attr', own_attr)

        def _get_context(self, name):
            # TODO: save parent context on self, so it must not be called every
            #       time
            oa = self._own_attr
            if name in oa:
                return self.context
            else:
                return IEventAccessor(aq_parent(self.context))

        def __getattr__(self, name):
            return getattr(self._get_context(name), name, None)

        def __setattr__(self, name, value):
            return setattr(self._get_context(name), name, value)

        def __delattr__(self, name):
            delattr(self._get_context(name), name)

        # R/O properties
        # TODO: Having uid here makes probably no sense, since Occurrences are
        #       created on the fly and not persistent.

        @property
        def url(self):
            return safe_unicode(self.context.absolute_url())

    class ImageScalingViewFactory(BrowserView):
        """Factory for ImageScaling view for occurrences.
        Delegates to AT or DX specific view and rebinds to the parent context.
        """
        def __new__(cls, context, request):
            parent = aq_parent(context)
            if IATEvent.providedBy(parent):
                return ATImageScaling(parent, request)
            elif IDXEvent.providedBy(parent):
                return DXImageScaling(parent, request)
            return None

### Navigation

- index
- modules |
- plone.app.event 1.0dev documentation »
- Module code »