Exemple #1
0
    def onExportIcalendarEvent(self, event):
        # triggered from "File | Import/Export" Menu

        wildcard = "iCalendar files|*.ics|All files (*.*)|*.*"
        dlg = wx.FileDialog(wx.GetApp().mainFrame, "Choose filename to export to",
                              "", "export.ics", wildcard,
                              wx.SAVE | wx.OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            (dir, filename) = os.path.split(dlg.GetPath())
            dlg.Destroy()
        else:
            dlg.Destroy()
            self.setStatusMessage("Export aborted")
            return

        self.setStatusMessage ("Exporting to %s" % filename)
        try:
            share = Sharing.OneTimeFileSystemShare(dir, filename,
                            ICalendar.ICalendarFormat, view=self.itsView)
            collection = ItemCollection(view=self.itsView)
            for event in Calendar.CalendarEvent.iterItems(self.itsView):
                collection.add(event)
            share.contents = collection
            share.put()
            self.setStatusMessage("Export completed")
        except:
            trace = "".join(traceback.format_exception (*sys.exc_info()))
            logger.info("Failed exportFile:\n%s" % trace)
            self.setStatusMessage("Export failed")
Exemple #2
0
    def onExportIcalendarEvent(self, event):
        # triggered from "File | Import/Export" Menu

        wildcard = "iCalendar files|*.ics|All files (*.*)|*.*"
        dlg = wx.FileDialog(
            wx.GetApp().mainFrame,
            "Choose filename to export to",
            "",
            "export.ics",
            wildcard,
            wx.SAVE | wx.OVERWRITE_PROMPT,
        )
        if dlg.ShowModal() == wx.ID_OK:
            (dir, filename) = os.path.split(dlg.GetPath())
            dlg.Destroy()
        else:
            dlg.Destroy()
            self.setStatusMessage("Export aborted")
            return

        self.setStatusMessage("Exporting to %s" % filename)
        try:
            share = Sharing.OneTimeFileSystemShare(dir, filename, ICalendar.ICalendarFormat, view=self.itsView)
            collection = ItemCollection(view=self.itsView)
            for event in Calendar.CalendarEvent.iterItems(self.itsView):
                collection.add(event)
            share.contents = collection
            share.put()
            self.setStatusMessage("Export completed")
        except:
            trace = "".join(traceback.format_exception(*sys.exc_info()))
            logger.info("Failed exportFile:\n%s" % trace)
            self.setStatusMessage("Export failed")
Exemple #3
0
def FindTrashCollection(view):
    """
    this is a really ugly hack - basically we have to iterate through
    all ItemCollections until we find the right one
    when the special collections stop being copied into the soup, this
    will just look like:
    return schema.ns("osaf.app", view).trash
    """
    for collection in ItemCollection.iterItems(view):
        if IsTrashCollection(collection):
            return collection
    assert False, "No Trash collection!"
Exemple #4
0
def FindTrashCollection(view):
    """
    this is a really ugly hack - basically we have to iterate through
    all ItemCollections until we find the right one
    when the special collections stop being copied into the soup, this
    will just look like:
    return schema.ns("osaf.app", view).trash
    """
    for collection in ItemCollection.iterItems(view):
        if IsTrashCollection(collection):
            return collection
    assert False, "No Trash collection!"
Exemple #5
0
 def sharedWebDAVCollections(self):
     # return the list of all the shared collections
     # @@@DLD - use new query, once it can handle method calls, or when our item.isShared
     #  attribute is correctly set.
     UseNewQuery = False
     if UseNewQuery:
         qString = u"for i in '//parcels/osaf/pim/ItemCollection' where len (i.sharedURL) > 0"
         collQuery = Query.Query(self.itsView.repository, qString)
         collQuery.recursive = False
         collections = list(collQuery)
     else:
         collections = [coll for coll in ItemCollection.iterItems(self.itsView) if Sharing.isShared(coll)]
     return collections
Exemple #6
0
    def writeICalendarUnicodeBug3338(self):
        event = Calendar.CalendarEvent(view = self.repo.view)
        event.displayName = u"unicode \u0633\u0644\u0627\u0645"
        event.startTime = datetime.datetime(2010, 1, 1, 10)
        event.endTime = datetime.datetime(2010, 1, 1, 11)

        coll = ItemCollection(name="testcollection", 
                                             parent=self.sandbox)
        coll.add(event)
        filename = "unicode_export.ics"

        conduit = Sharing.FileSystemConduit(name="conduit", sharePath=".",
                            shareName=filename, view=self.repo.view)
        format = ICalendar.ICalendarFormat(name="format", view=self.repo.view)
        self.share = Sharing.Share(name="share",contents=coll, conduit=conduit,
                                    format=format, view=self.repo.view)
        if self.share.exists():
            self.share.destroy()
        self.share.create()
        self.share.put()
        cal=vobject.readComponents(file(filename, 'rb')).next()
        self.assertEqual(cal.vevent[0].summary[0].value, event.displayName)
        self.share.destroy()
Exemple #7
0
 def sharedWebDAVCollections (self):
     # return the list of all the shared collections
     # @@@DLD - use new query, once it can handle method calls, or when our item.isShared
     #  attribute is correctly set.
     UseNewQuery = False
     if UseNewQuery:
         qString = u"for i in '//parcels/osaf/pim/ItemCollection' where len (i.sharedURL) > 0"
         collQuery = Query.Query (self.itsView.repository, qString)
         collQuery.recursive = False
         collections = list(collQuery)
     else:
         collections = [
             coll for coll in ItemCollection.iterItems(self.itsView)
                  if Sharing.isShared(coll)
         ]
     return collections
Exemple #8
0
    def _createCollection(self, repo):
        sandbox = repo.findPath("//sandbox")

        coll = ItemCollection(name="testcollection",
         parent=sandbox)
Exemple #9
0
    def _mapItemToCacheKeyItem(self, item):
        key = item
        rerender = False
        sidebar = Block.Block.findBlockByName ("Sidebar")
        """
          collectionList should be in the order that the source items are overlayed in the Calendar view
        """
        collectionList = [theItem for theItem in sidebar.contents if (theItem in sidebar.checkedItems) and (theItem is not item)]
        if isinstance (item, ItemCollection):
            collectionList.insert (0, item)
        if len (collectionList) > 0:
            """
              tupleList is sorted so we always end up with on collection for any order of collections
            in the source
            """
            tupleList = [theItem.itsUUID for theItem in collectionList]
            tupleList.sort()

            filterKind = sidebar.filterKind
            if not filterKind is None:
                tupleList.append (filterKind.itsUUID)
            
            if len (tupleList) > 1:
                tupleKey = tuple (tupleList)

                try:
                    key = self.itemTupleKeyToCacheKey [tupleKey]
                except KeyError:
                    """
                      We need to make a new filtered item collection that depends
                      upon the unfiltered collection. Unfortunately, making a new
                      ItemCollection with a rule whose results include all items
                      in the original ItemCollection has a problem: when the results
                      in the unfiltered ItemCollection change we don't get notified.

                      Alternatively we make a copy of the ItemCollection (and it's
                      rule) which has another problem: When the rule in the original
                      ItemCollection change we don't update our copied rule.                      
                    """
                    key = ItemCollection(view=self.itsView)
                    key.source = collectionList
                    
                    displayName = u" and ".join ([theItem.displayName for theItem in collectionList])
                    if filterKind is not None:
                        key.addFilterKind (filterKind)
                        displayName += u" filtered by " + filterKind.displayName                    
                    key.displayName = displayName

                    self.itemTupleKeyToCacheKey [tupleKey] = key
                else:
                    """
                      Check to see if we need to reorder the source list. The list is kept
                    sorted by the order of the collections as they overlay one another.
                      We don't bother to sort when we're looking up a collection that isn't
                    displayed in the summary view, both because it's not necessary and because
                    it causes the source attribute to change which causes a notification to
                    update the sidebar, which causes the order to change, causing a
                    notification, ... repeating forever.
                    """
                    if sidebar.selectedItemToView is item:
                        for new, old in map (None, key.source, collectionList):
                            if new is not old:
                                key.source = collectionList
                                rerender = True
                                break
        return key, rerender
Exemple #10
0
    def importProcess(self, text, extension=None, item=None):
        # the item parameter is so that a share item can be passed in for us
        # to populate.

        # An ICalendar file doesn't have any 'share' info, just the collection
        # of events, etc.  Therefore, we want to actually populate the share's
        # 'contents':

        view = self.itsView
        filters = self.share.filterAttributes

        newItemParent = self.findPath("//userdata")
        eventKind = self.itsView.findPath(self._calendarEventPath)
        taskKind = self.itsView.findPath(self._taskPath)
        textKind = self.itsView.findPath(self._lobPath)

        if self.fileStyle() == self.STYLE_SINGLE:
            if item is None:
                item = ItemCollection(view=view)
            elif isinstance(item, Sharing.Share):
                if item.contents is None:
                    item.contents = ItemCollection(view=view)
                item = item.contents

            if not isinstance(item, ItemCollection):
                print "Only a share or an item collection can be passed in"
                #@@@MOR Raise something

        input = StringIO.StringIO(text)
        calendar = vobject.readComponents(input, validate=True).next()

        if self.fileStyle() == self.STYLE_SINGLE:
            try:
                calName = calendar.contents[u'x-wr-calname'][0].value
            except:
                calName = "Imported Calendar"
            item.displayName = calName

        countNew = 0
        countUpdated = 0

        eventlist = getattr(calendar, 'vevent', [])
        todolist = getattr(calendar, 'vtodo', [])

        # this is just a quick hack to get VTODO working, FIXME write
        # more readable table driven code to process VEVENTs and VTODOs
        for event in itertools.chain(eventlist, todolist):
            vtype = event.name
            if vtype == u'VEVENT':
                logger.debug("got VEVENT")
                pickKind = eventKind
            elif vtype == u'VTODO':
                logger.debug("got VTODO")
                pickKind = taskKind

            try:
                displayName = event.summary[0].value
            except AttributeError:
                displayName = ""

            try:
                description = event.description[0].value
            except AttributeError:
                description = None

            try:
                location = event.location[0].value
            except AttributeError:
                location = None

            try:
                status = event.status[0].value.lower()
                if status in ('confirmed', 'tentative'):
                    pass
                elif status == 'cancelled':  #Chandler doesn't have CANCELLED
                    status = 'fyi'
                else:
                    status = 'confirmed'
            except AttributeError:
                status = 'confirmed'

            try:
                # FIXME assumes DURATION, not DATE-TIME
                reminderDelta = event.valarm[0].trigger[0].value
            except AttributeError:
                reminderDelta = None

            # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess
            # what that would mean, so we won't catch an exception if there's no
            # dtstart.
            dtstart = event.dtstart[0].value

            try:
                duration = event.duration[0].value
            except AttributeError:
                # note that duration = dtend - dtstart isn't strictly correct
                # throughout a recurrence set, 1 hour differences might happen
                # around DST, but we'll ignore that corner case for now
                try:
                    duration = event.dtend[0].value - dtstart
                # FIXME no end time or duration, Calendar UI doesn't seem to
                # like events with no duration, so for now we'll set a dummy
                # duration of 1 hour
                except AttributeError:
                    # FIXME Nesting try/excepts is ugly.  Also, we're assuming
                    # DATE-TIMEs, not DATEs.
                    try:
                        duration = event.due[0].value - dtstart
                    except AttributeError:
                        if vtype == u'VEVENT':
                            duration = datetime.timedelta(hours=1)
                        elif vtype == u'VTODO':
                            duration = None

            isDate = type(dtstart) == date
            if isDate:
                dtstart = datetime.datetime.combine(dtstart, time(0))
                if duration:  # convert to Chandler's notion of all day duration
                    duration -= datetime.timedelta(days=1)

            # ignore timezones and recurrence till tzinfo -> PyICU is written
            # give the repository a naive datetime, no timezone
            dtstart = Recurrence.stripTZ(dtstart)

            # See if we have a corresponding item already
            recurrenceID = None
            uidMatchItem = self.findUID(event.uid[0].value)
            if uidMatchItem is not None:
                logger.debug("matched UID")
                try:
                    recurrenceID = event.contents['recurrence-id'][0].value
                    if type(recurrenceID) == date:
                        recurrenceID = datetime.datetime.combine(
                            recurrenceID, time(0))
                    else:
                        recurrenceID = Recurrence.stripTZ(recurrenceID)
                except:
                    pass
                if recurrenceID:
                    eventItem = uidMatchItem.getRecurrenceID(recurrenceID)
                    if eventItem == None:
                        raise Exception, "RECURRENCE-ID didn't match rule. " + \
                                         "RECURRENCE-ID = %s" % recurrenceID
                else:
                    eventItem = uidMatchItem
                    countUpdated += 1
            else:
                eventItem = pickKind.newItem(None, newItemParent)
                countNew += 1
                eventItem.icalUID = event.uid[0].value

            # vobject isn't meshing well with dateutil when dtstart isDate;
            # dtstart is converted to a datetime for dateutil, but rdate
            # isn't.  To make dateutil happy, convert rdates which are dates to
            # datetimes until vobject is fixed.
            for i, rdate in enumerate(event.rdate):
                if type(rdate) == date:
                    event.rdate[i] = datetime.datetime.combine(rdate, time(0))
                else:
                    event.rdate[i] = Recurrence.stripTZ(event.rdate[i])

                # get rid of RDATES that match dtstart, created by vobject to
                # deal with unusual RRULEs correctly
                if event.rdate[i] == dtstart:
                    del event.rdate[i]

            logger.debug("eventItem is %s" % str(eventItem))

            #Default to NOT any time
            eventItem.anyTime = False

            eventItem.displayName = displayName
            if isDate:
                eventItem.allDay = True
            eventItem.startTime = dtstart
            if vtype == u'VEVENT':
                eventItem.endTime = dtstart + duration
            elif vtype == u'VTODO':
                if duration is not None:
                    eventItem.dueDate = dtstart + duration

            if not filters or "transparency" not in filters:
                eventItem.transparency = status

            # I think Item.description describes a Kind, not userdata, so
            # I'm using DESCRIPTION <-> body
            if description is not None:
                eventItem.body = textKind.makeValue(description)

            if location:
                eventItem.location = Calendar.Location.getLocation(
                    view, location)

            if not filters or "reminderTime" not in filters:
                if reminderDelta is not None:
                    eventItem.reminderTime = dtstart + reminderDelta

            if len(event.rdate) > 0 or len(event.rrule) > 0:
                eventItem.setRuleFromDateUtil(event.rruleset)
            elif recurrenceID is None:  # delete any existing rule
                eventItem.removeRecurrence()

            logger.debug("Imported %s %s" %
                         (eventItem.displayName, eventItem.startTime))

            if self.fileStyle() == self.STYLE_SINGLE:
                item.add(eventItem)
            else:
                return eventItem

        logger.info("...iCalendar import of %d new items, %d updated" % \
         (countNew, countUpdated))

        return item
Exemple #11
0
    def importProcess(self, text, extension=None, item=None):
        # the item parameter is so that a share item can be passed in for us
        # to populate.

        # An ICalendar file doesn't have any 'share' info, just the collection
        # of events, etc.  Therefore, we want to actually populate the share's
        # 'contents':

        view = self.itsView
        filters = self.share.filterAttributes

        newItemParent = self.findPath("//userdata")
        eventKind = self.itsView.findPath(self._calendarEventPath)
        taskKind  = self.itsView.findPath(self._taskPath)
        textKind  = self.itsView.findPath(self._lobPath)

        if self.fileStyle() == self.STYLE_SINGLE:
            if item is None:
                item = ItemCollection(view=view)
            elif isinstance(item, Sharing.Share):
                if item.contents is None:
                    item.contents = ItemCollection(view=view)
                item = item.contents

            if not isinstance(item, ItemCollection):
                print "Only a share or an item collection can be passed in"
                #@@@MOR Raise something

        input = StringIO.StringIO(text)
        calendar = vobject.readComponents(input, validate=True).next()

        if self.fileStyle() == self.STYLE_SINGLE:
            try:
                calName = calendar.contents[u'x-wr-calname'][0].value
            except:
                calName = "Imported Calendar"
            item.displayName = calName

        countNew = 0
        countUpdated = 0
        
        eventlist = getattr(calendar, 'vevent', [])
        todolist  = getattr(calendar, 'vtodo', [])
        
        # this is just a quick hack to get VTODO working, FIXME write
        # more readable table driven code to process VEVENTs and VTODOs
        for event in itertools.chain(eventlist, todolist):
            vtype = event.name
            if vtype == u'VEVENT':
                logger.debug("got VEVENT")
                pickKind = eventKind
            elif vtype == u'VTODO':
                logger.debug("got VTODO")
                pickKind = taskKind

            try:
                displayName = event.summary[0].value
            except AttributeError:
                displayName = ""

            try:
                description = event.description[0].value
            except AttributeError:
                description = None
                
            try:
                location = event.location[0].value
            except AttributeError:
                location = None            

            try:
                status = event.status[0].value.lower()
                if status in ('confirmed', 'tentative'):
                    pass
                elif status == 'cancelled': #Chandler doesn't have CANCELLED
                    status = 'fyi'
                else:
                    status = 'confirmed'
            except AttributeError:
                status = 'confirmed'

            try:
                # FIXME assumes DURATION, not DATE-TIME
                reminderDelta = event.valarm[0].trigger[0].value
            except AttributeError:
                reminderDelta = None

            # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess
            # what that would mean, so we won't catch an exception if there's no
            # dtstart.
            dtstart  = event.dtstart[0].value 
            
            try:
                duration = event.duration[0].value
            except AttributeError:
                # note that duration = dtend - dtstart isn't strictly correct
                # throughout a recurrence set, 1 hour differences might happen
                # around DST, but we'll ignore that corner case for now
                try:
                    duration = event.dtend[0].value - dtstart
                # FIXME no end time or duration, Calendar UI doesn't seem to
                # like events with no duration, so for now we'll set a dummy
                # duration of 1 hour
                except AttributeError:
                    # FIXME Nesting try/excepts is ugly.  Also, we're assuming
                    # DATE-TIMEs, not DATEs.
                    try:
                        duration = event.due[0].value - dtstart
                    except AttributeError:
                        if vtype == u'VEVENT':
                            duration = datetime.timedelta(hours=1)
                        elif vtype == u'VTODO':
                            duration = None
                            
            isDate = type(dtstart) == date
            if isDate:
                dtstart = datetime.datetime.combine(dtstart, time(0))
                if duration: # convert to Chandler's notion of all day duration
                    duration -= datetime.timedelta(days=1)
                    
            # ignore timezones and recurrence till tzinfo -> PyICU is written
            # give the repository a naive datetime, no timezone
            dtstart = Recurrence.stripTZ(dtstart)
            
            # See if we have a corresponding item already
            recurrenceID = None
            uidMatchItem = self.findUID(event.uid[0].value)
            if uidMatchItem is not None:
                logger.debug("matched UID")
                try:
                    recurrenceID = event.contents['recurrence-id'][0].value
                    if type(recurrenceID) == date:
                        recurrenceID = datetime.datetime.combine(recurrenceID,
                                                                 time(0))
                    else:
                        recurrenceID = Recurrence.stripTZ(recurrenceID)
                except:
                    pass
                if recurrenceID:
                    eventItem = uidMatchItem.getRecurrenceID(recurrenceID)
                    if eventItem == None:
                        raise Exception, "RECURRENCE-ID didn't match rule. " + \
                                         "RECURRENCE-ID = %s" % recurrenceID
                else:
                    eventItem = uidMatchItem
                    countUpdated += 1
            else:
                eventItem = pickKind.newItem(None, newItemParent)
                countNew += 1
                eventItem.icalUID = event.uid[0].value
            

            # vobject isn't meshing well with dateutil when dtstart isDate;
            # dtstart is converted to a datetime for dateutil, but rdate
            # isn't.  To make dateutil happy, convert rdates which are dates to
            # datetimes until vobject is fixed.
            for i, rdate in enumerate(event.rdate):
                if type(rdate) == date:
                    event.rdate[i] = datetime.datetime.combine(rdate, time(0))
                else:
                    event.rdate[i] = Recurrence.stripTZ(event.rdate[i])
                    
                # get rid of RDATES that match dtstart, created by vobject to
                # deal with unusual RRULEs correctly
                if event.rdate[i] == dtstart:
                    del event.rdate[i]
                
            logger.debug("eventItem is %s" % str(eventItem))
            
            #Default to NOT any time
            eventItem.anyTime = False
            
            eventItem.displayName = displayName
            if isDate:
                eventItem.allDay = True
            eventItem.startTime   = dtstart
            if vtype == u'VEVENT':
                eventItem.endTime = dtstart + duration
            elif vtype == u'VTODO':
                if duration is not None:
                    eventItem.dueDate = dtstart + duration
            
            if not filters or "transparency" not in filters:
                eventItem.transparency = status
            
            # I think Item.description describes a Kind, not userdata, so
            # I'm using DESCRIPTION <-> body  
            if description is not None:
                eventItem.body = textKind.makeValue(description)
            
            if location:
                eventItem.location = Calendar.Location.getLocation(view,
                                                                   location)
            
            if not filters or "reminderTime" not in filters:
                if reminderDelta is not None:
                    eventItem.reminderTime = dtstart + reminderDelta

            if len(event.rdate) > 0 or len(event.rrule) > 0:
                eventItem.setRuleFromDateUtil(event.rruleset)
            elif recurrenceID is None: # delete any existing rule
                eventItem.removeRecurrence()

            logger.debug("Imported %s %s" % (eventItem.displayName,
             eventItem.startTime))

            if self.fileStyle() == self.STYLE_SINGLE:
                item.add(eventItem)
            else:
                return eventItem
                 
        logger.info("...iCalendar import of %d new items, %d updated" % \
         (countNew, countUpdated))

        return item