Esempio n. 1
0
class BaseConduit(Conduit):

    sharePath = schema.One(
        schema.Text,
        initialValue=u"",
        doc="The parent 'directory' of the share",
    )

    shareName = schema.One(
        schema.Text,
        initialValue=u"",
        doc="The 'directory' name of the share, relative to 'sharePath'",
    )

    schema.initialValues(shareName=lambda self: unicode(UUID()), )

    # TODO: see if this is used anymore:

    def isAttributeModifiable(self, item, attribute):
        share = self.share

        if utility.isSharedByMe(share) or share.mode in ('put', 'both'):
            return True

        # In old style shares, an attribute isn't modifiable if it's one
        # of the attributes shared for this item in this share
        for attr in item.getBasedAttributes(attribute):
            if attr in share.getSharedAttributes(item.itsKind):
                return False

        return True
Esempio n. 2
0
class ClientIdentifier(pim.ContentItem):
    clientID = schema.One(
        schema.Bytes,
        doc='Used to identify different running instances of Chandler')

    schema.initialValues(
        clientID=lambda self: hashlib.sha256(self.itsUUID.str16()).hexdigest())
class Account(schema.Item):

    userid = schema.One(schema.Text)
    server = schema.One(schema.Importable)
    protocol = schema.One(schema.Symbol)
    user = schema.One(otherName='accounts', initialValue=None)
    default = schema.One(schema.Boolean, initialValue=False)
    autoLogin = schema.One(schema.Boolean, initialValue=False)
    conduits = schema.Sequence(otherName='account')

    _clients = {}

    schema.initialValues(
        user=lambda self: User(itsView=self.itsView, name=self.userid))

    def login(self, printf, autoLogin=False):
        raise NotImplementedError, "%s.login" % (type(self))

    def isLoggedIn(self):
        raise NotImplementedError, "%s.isLoggedIn" % (type(self))

    def subscribe(self, name, remoteId):
        raise NotImplementedError, "%s.subscribe" % (type(self))

    def sync(self, share):
        raise NotImplementedError, "%s.sync" % (type(self))

    def _getClient(self):
        return type(self)._clients.get(self.itsUUID)

    def _setClient(self, client):
        type(self)._clients[self.itsUUID] = client

    client = property(_getClient, _setClient)
Esempio n. 4
0
class MailShare(Share):

    schema.initialValues(
        conduit=lambda self: MailConduit(
            itsParent=self, peerId=self.peerId, account=self.account),
        format=lambda self: CloudXMLDiffFormat(itsParent=self))

    def sync(self, modeOverride=None, activity=None, forceUpdate=None):

        account = self.conduit.account
        account.login(None)
        return account.sync(self)
class PreviewArea(CalendarRangeBlock):
    timeCharacterStyle = schema.One(Styles.CharacterStyle)
    eventCharacterStyle = schema.One(Styles.CharacterStyle)
    linkCharacterStyle = schema.One(Styles.CharacterStyle)

    miniCalendar = schema.One(inverse=MiniCalendar.previewArea,
                              defaultValue=None)

    schema.addClouds(copying=schema.Cloud(
        byRef=[timeCharacterStyle, eventCharacterStyle, linkCharacterStyle],
        byCloud=[miniCalendar]))

    schema.initialValues(rangeIncrement=lambda self: one_day)

    def onSetContentsEvent(self, event):
        #We want to ignore, because view changes could come in here, and we
        #never want to change our collection
        pass

    def onSelectAllEventUpdateUI(self, event):
        event.arguments['Enable'] = False

    def instantiateWidget(self):
        if not self.getHasBeenRendered():
            self.setRange(datetime.now().date())
            self.setHasBeenRendered()
        if wx.Platform == '__WXMAC__':
            # on the Mac, borders around the minical and preview area look weird,
            # but we want one around our parent.  Modifying our parent is quite
            # a hack, but it works rather nicely.
            self.parentBlock.widget.SetWindowStyle(wx.BORDER_SIMPLE)
        return wxPreviewArea(self.parentBlock.widget,
                             self.getWidgetID(),
                             timeCharStyle=self.timeCharacterStyle,
                             eventCharStyle=self.eventCharacterStyle,
                             linkCharStyle=self.linkCharacterStyle)

    def onItemNotification(self, notificationType, data):
        # Delegate notifications to the block
        self.widget.onItemNotification(notificationType, data)

    def activeViewChanged(self):
        self.synchronizeWidget()
Esempio n. 6
0
class ContentItem(Triageable):
    """
    Content Item

    Content Item is the abstract super-kind for things like Contacts, Calendar
    Events, Tasks, Mail Messages, and Notes.  Content Items are user-level
    items, which a user might file, categorize, share, and delete.

    Examples:
     - a Calendar Event -- 'Lunch with Tug'
     - a Contact -- 'Terry Smith'
     - a Task -- 'mail 1040 to IRS'
    """
    isProxy = False

    displayName = schema.One(LocalizableString, defaultValue=u"", indexed=True)
    body = schema.One(
        LocalizableString,
        indexed=True,
        defaultValue=u"",
        doc="All Content Items may have a body to contain notes.  It's "
        "not decided yet whether this body would instead contain the "
        "payload for resource items such as presentations or "
        "spreadsheets -- resource items haven't been nailed down "
        "yet -- but the payload may be different from the notes because "
        "payload needs to know MIME type, etc.")

    creator = schema.One(
        # Contact
        doc="Link to the contact who created the item.")

    modifiedFlags = schema.Many(
        Modification,
        defaultValue=Empty,
        description='Used to track the modification state of the item')

    lastModified = schema.One(
        schema.DateTimeTZ,
        doc="DateTime (including timezone) this item was last modified",
        defaultValue=None,
    )

    lastModifiedBy = schema.One(
        doc="Link to the EmailAddress who last modified the item.",
        defaultValue=None)

    lastModification = schema.One(
        Modification,
        doc="What the last modification was.",
        defaultValue=Modification.created,
    )

    BYLINE_FORMATS = {
        Modification.created: (
            _(u"Created by %(user)s on %(date)s %(tz)s"),
            _(u"Created on %(date)s %(tz)s"),
        ),
        Modification.edited: (
            _(u"Edited by %(user)s on %(date)s %(tz)s"),
            _(u"Edited on %(date)s %(tz)s"),
        ),
        Modification.updated: (
            _(u"Updated by %(user)s on %(date)s %(tz)s"),
            _(u"Updated on %(date)s %(tz)s"),
        ),
        Modification.sent: (
            _(u"Sent by %(user)s on %(date)s %(tz)s"),
            _(u"Sent on %(date)s %(tz)s"),
        ),
        Modification.queued: (
            _(u"Queued by %(user)s on %(date)s %(tz)s"),
            _(u"Queued on %(date)s %(tz)s"),
        ),
    }

    def getByline(self):
        lastModification = self.lastModification
        assert lastModification in self.BYLINE_FORMATS

        fmt, noUserFmt = self.BYLINE_FORMATS[lastModification]

        # fall back to createdOn
        view = self.itsView
        lastModified = (self.lastModified or getattr(self, 'createdOn', None)
                        or datetime.now(view.tzinfo.default))

        shortDateTimeFormat = schema.importString(
            "osaf.pim.shortDateTimeFormat")
        date = shortDateTimeFormat.format(view, lastModified)

        tzPrefs = schema.ns('osaf.pim', view).TimezonePrefs
        if tzPrefs.showUI:
            from calendar.TimeZone import shortTZ
            tzName = shortTZ(view, lastModified)
        else:
            tzName = u''

        user = self.lastModifiedBy
        if user:
            result = fmt % dict(user=user.getLabel(), date=date, tz=tzName)
        else:
            result = noUserFmt % dict(date=date, tz=tzName)
        return result.strip()

    error = schema.One(
        schema.Text,
        doc="A user-visible string containing the last error that occurred. "
        "Typically, this should be set by the sharing or email layers when "
        "a conflict or delivery problem occurs.",
        defaultValue=None)

    byline = schema.Calculated(schema.Text,
                               basedOn=(modifiedFlags, lastModified,
                                        lastModification, lastModifiedBy),
                               fget=getByline)

    importance = schema.One(
        ImportanceEnum,
        doc="Most items are of normal importance (no value need be shown), "
        "however some things may be flagged either highly important or "
        "merely 'fyi'. This attribute is also used in the mail schema, so "
        "we shouldn't make any changes here that would break e-mail "
        "interoperability features.",
        defaultValue="normal",
    )

    mine = schema.One(schema.Boolean, defaultValue=True)

    private = schema.One(schema.Boolean, defaultValue=False)

    read = schema.One(schema.Boolean,
                      defaultValue=False,
                      doc="A flag indicating whether the this item has "
                      "been 'viewed' by the user")

    needsReply = schema.One(
        schema.Boolean,
        defaultValue=False,
        doc="A flag indicating that the user wants to reply to this item")

    createdOn = schema.One(schema.DateTimeTZ,
                           doc="DateTime this item was created")

    # ContentItem instances can be put into ListCollections and AppCollections
    collections = schema.Sequence(
        notify=True)  # inverse=collections.inclusions

    # ContentItem instances can be excluded by AppCollections
    excludedBy = schema.Sequence()

    # ContentItem instances can be put into SmartCollections (which define
    # the other end of this biref)
    appearsIn = schema.Sequence()

    # The date used for sorting the Date column
    displayDate = schema.One(schema.DateTimeTZ, indexed=True)
    displayDateSource = schema.One(schema.Importable)

    # The value displayed (and sorted) for the Who column.
    displayWho = schema.One(schema.Text, indexed=True)
    displayWhoSource = schema.One(schema.Importable)

    schema.addClouds(sharing=schema.Cloud(
        literal=["displayName", body, createdOn, "description"],
        byValue=[lastModifiedBy]),
                     copying=schema.Cloud())

    schema.initialValues(
        createdOn=lambda self: datetime.now(self.itsView.tzinfo.default))

    def __str__(self):
        if self.isStale():
            return super(ContentItem, self).__str__()
            # Stale items can't access their attributes

        return self.__unicode__().encode('utf8')

    def __unicode__(self):
        if self.isStale():
            return super(ContentItem, self).__unicode__()

        return unicode(
            getattr(self, 'displayName', self.itsName) or self.itsUUID.str64())

    def InitOutgoingAttributes(self):
        """ Init any attributes on ourself that are appropriate for
        a new outgoing item.
        """

        super(ContentItem, self).InitOutgoingAttributes()

        # default the displayName
        self.displayName = messages.UNTITLED

        if not self.hasLocalAttributeValue('lastModifiedBy'):
            self.lastModifiedBy = self.getMyModifiedByAddress()

    def ExportItemData(self, clipboardHandler):
        # Create data for this kind of item in the clipboard handler
        # The data is used for Drag and Drop or Cut and Paste
        try:
            super(ContentItem, self).ExportItemData(clipboardHandler)
        except AttributeError:
            pass

        # Let the clipboard handler know we've got a ContentItem to export
        clipboardHandler.ExportItemFormat(self, 'ContentItem')

    def onItemDelete(self, view, deferring):
        # Hook for stamp deletion ...
        from stamping import Stamp
        for stampObject in Stamp(self).stamps:
            onItemDelete = getattr(stampObject, 'onItemDelete', None)
            if onItemDelete is not None:
                onItemDelete(view, deferring)
        super(ContentItem, self).onItemDelete(view, deferring)

    def addToCollection(self, collection):
        """Add self to the given collection.

        For most items, just call collection.add(self), but for recurring
        events, this method is intercepted by a proxy and buffered while the
        user selects from various possible meanings for adding a recurring event
        to a collection.

        """
        collection.add(self)

    def removeFromCollection(self, collection):
        """Remove self from the given collection.

        For most items, just call collection.remove(self), but for recurring
        events, this method is intercepted by a proxy and buffered while the
        user selects from various possible meanings for removing a recurring
        event from a collection.

        The special mine collection behavior that removed items should remain in
        the Dashboard is implemented here.

        """
        self._prepareToRemoveFromCollection(collection)
        collection.remove(self)

    def _prepareToRemoveFromCollection(self, collection):
        """
        If the collection is a mine collection and the item doesn't exist in any
        other 'mine' collections, manually add it to 'all' to keep the item
        'mine'.

        We don't want to do this blindly though, or all's inclusions will get
        unnecessarily full.

        We also don't want to remove collection from mine.sources. That will
        cause a notification storm as items temporarily leave and re-enter
        being 'mine'.
        
        """
        pim_ns = schema.ns('osaf.pim', self.itsView)
        mine = pim_ns.mine
        allCollection = pim_ns.allCollection

        if collection in mine.sources:
            for otherCollection in self.appearsIn:
                if otherCollection is collection:
                    continue

                if otherCollection in mine.sources:
                    # we found it in another 'mine'
                    break
            else:
                # we didn't find it in a 'mine' Collection
                self.collections.add(allCollection)

    def getMembershipItem(self):
        """ Get the item that should be used to test for membership
        tests i.e. if item in collection: should be if
        item.getMembershipItem() in collection

        For most items, this is just itself, but for recurring
        events, this method is intercepted by a proxy.
        """
        return self

    def changeEditState(self,
                        modType=Modification.edited,
                        who=None,
                        when=None):
        """
        @param modType: What kind of modification you are making. Used to set
                        the value of C{self.lastModification}.
        @type modType: C{Modification}
        
        @param who: May be C{None}, which is interpreted as an anonymous
                    user (e.g. a "drive-by" sharing user).
                    Used to set the value of {self.lastModifiedBy}.
        @type who: C{EmailAddress}
        
        @param when: The date&time of this change. Used to set the value
                     of C{self.lastModified}. The default, C{None}, sets
                     the 
        @type when: C{datetime}
        """

        logger.debug(
            "ContentItem.changeEditState() self=%s view=%s modType=%s who=%s when=%s",
            self, self.itsView, modType, who, when)

        currentModFlags = self.modifiedFlags

        if (modType == Modification.edited
                and not self.hasLocalAttributeValue('lastModification', None)
                and
                not getattr(self, 'inheritFrom',
                            self).hasLocalAttributeValue('lastModification')):
            # skip edits until the item is explicitly marked created

            return

        if modType == Modification.sent:
            if Modification.sent in currentModFlags:
                #raise ValueError, "You can't send an item twice"
                pass
            elif Modification.queued in currentModFlags:
                currentModFlags.remove(Modification.queued)
        elif modType == Modification.updated:
            #XXX Brian K: an update can occur with out a send.
            # Case user a sends a item to user b (state == sent)
            #      user a edits item to user b and adds user c (state == update).
            # For user c the state of sent was never seen.

            #if not Modification.sent in currentModFlags:
            #    raise ValueError, "You can't update an item till it's been sent"

            if Modification.queued in currentModFlags:
                currentModFlags.remove(Modification.queued)

        # Clear the edited flag and error on send/update/queue
        if (modType in (Modification.sent, Modification.updated,
                        Modification.queued)):
            if Modification.edited in currentModFlags:
                currentModFlags.remove(Modification.edited)
            del self.error

        if not currentModFlags:
            self.modifiedFlags = set([modType])
        else:
            currentModFlags.add(modType)
        self.lastModification = modType
        self.lastModified = when or datetime.now(self.itsView.tzinfo.default)
        self.lastModifiedBy = who  # None => me

    """
    ACCESSORS

    Accessors for Content Item attributes
    """

    def getEmailAddress(self, nameOrAddressString):
        """
          Lookup or create an EmailAddress based
        on the supplied string.
        This method is here for convenient access, so users
        don't need to import Mail.
        """
        from mail import EmailAddress
        return EmailAddress.getEmailAddress(self.itsView, nameOrAddressString)

    def getCurrentMeEmailAddress(self):
        """
        Lookup or create a current "me" EmailAddress.
        This method is here for convenient access, so users
        don't need to import Mail.
        """
        import mail
        return mail.getCurrentMeEmailAddress(self.itsView)

    def getMyModifiedByAddress(self):
        """
        Get an EmailAddress that represents the local user, for
        storing in lastModifiedBy after a local change.
        """
        me = self.getCurrentMeEmailAddress()
        if not me:  # Email not configured...
            # Get the user name associated with the default sharing account
            import osaf.sharing  # hmm, this import seems wrong
            sharingAccount = osaf.sharing.getDefaultAccount(self.itsView)
            if sharingAccount is not None:
                import mail
                me = mail.EmailAddress.getEmailAddress(self.itsView,
                                                       sharingAccount.username)
        return me

    def _updateCommonAttribute(self,
                               attributeName,
                               sourceAttributeName,
                               collectorMethodName,
                               args=()):
        """
        Mechanism for coordinating updates to a common-display field
        (like displayWho and displayDate, but not displayName for now).
        """
        if self.isDeleted():
            return
        logger.debug("Collecting relevant %ss for %r %s", attributeName, self,
                     self)

        collectorMethod = getattr(type(self), collectorMethodName)

        # Collect possible values. The collector method adds tuples to the
        # contenders list; each tuple starts with a value to sort by, and
        # ends with the attribute value to assign and the name of the attribute
        # it came from (which will be used later to pick a displayable string
        # to describe the source of the value).
        #
        # Examples: if we sort by the attribute value, only two values are
        # needed in the tuple: eg, (aDate, 'dateField). If the picking happens
        # based on an additional value (or values), the sort value(s) come
        # before the attribute value: eg (1, '*****@*****.**', 'to'),
        # (2, '*****@*****.**', 'from'). This works because sort sorts by the
        # values in order, and we access the attribute value and name using
        # negative indexes (so contender[-2] is the value, contender[-1] is
        # the name).
        contenders = []
        collectorMethod(self, contenders, *args)

        # Now that we have the contenders, pick one.
        contenderCount = len(contenders)
        if contenderCount == 0:
            # No contenders: delete the value
            if hasattr(self, attributeName):
                delattr(self, attributeName)
            if hasattr(self, sourceAttributeName):
                delattr(self, sourceAttributeName)
            logger.debug("No relevant %s for %r %s", attributeName, self, self)
            return

        if contenderCount > 1:
            # We have more than one possibility: sort, then we'll use the first one.
            contenders.sort()
        result = contenders[0]
        logger.debug("Relevant %s for %r %s is %s", attributeName, self, self,
                     result)
        assert result[-2] is not None
        setattr(self, attributeName, result[-2])
        setattr(self, sourceAttributeName, result[-1])

        if getattr(self, 'inheritFrom', None) is None:
            for item in getattr(self, 'inheritTo', []):
                item._updateCommonAttribute(attributeName, sourceAttributeName,
                                            collectorMethodName, args)

    def addDisplayWhos(self, whos):
        pass

    def updateDisplayWho(self, op, attr):
        self._updateCommonAttribute('displayWho', 'displayWhoSource',
                                    'addDisplayWhos')

    def addDisplayDates(self, dates, now):
        super(ContentItem, self).addDisplayDates(dates, now)
        # Add our creation and last-mod dates, if they exist.
        for importance, attr in (999, "lastModified"), (1000, "createdOn"):
            v = getattr(self, attr, None)
            if v is not None:
                dates.append((importance, v, attr))

    def updateDisplayDate(self, op, attr):
        now = datetime.now(tz=self.itsView.tzinfo.default)
        self._updateCommonAttribute('displayDate', 'displayDateSource',
                                    'addDisplayDates', [now])

    @schema.observer(modifiedFlags, lastModified, createdOn)
    def onCreatedOrLastModifiedChanged(self, op, attr):
        self.updateDisplayDate(op, attr)

    @schema.observer(modifiedFlags, lastModification, lastModifiedBy, read)
    def onModificationChange(self, op, name):
        # CommunicationStatus might have changed
        self.updateDisplayWho(op, name)

    @schema.observer(error)
    def onErrorChanged(self, op, attr):
        # Pop to now if error is anything non-False-ish (a non-empty
        # string, etc)
        if getattr(self, 'error', None):
            self.setTriageStatus(None, popToNow=True, force=True)

    def getBasedAttributes(self, attribute):
        """ Determine the schema attributes that affect this attribute
        (which might be a Calculated attribute) """
        # If it's Calculated, see what it's based on;
        # otherwise, just return a list containing its own name.
        descriptor = getattr(self.__class__, attribute, None)
        try:
            basedOn = descriptor.basedOn
        except AttributeError:
            return (attribute, )
        else:
            return tuple(desc.name for desc in basedOn)

    def isAttributeModifiable(self, attribute):
        """ Determine if an item's attribute is modifiable based on the
            shares it's in """
        from osaf.sharing import isReadOnly
        return not isReadOnly(self)
Esempio n. 7
0
class JabberAccount(Account):

    useSSL = schema.One(schema.Boolean, initialValue=False)
    password = schema.One(schema.Text)
    resource = schema.One(schema.Text, initialValue='chandler')

    schema.initialValues(protocol=lambda self: 'jabber')

    def isLoggedIn(self):

        client = self.client
        return client is not None and client.connected

    def getId(self):

        if '@' in self.userid:
            return "%s/%s" % (self.userid, self.resource)
        else:
            return "%s@%s/%s" % (self.userid, self.server, self.resource)

    def login(self, printf, autoLogin=False):

        client = self.client

        if client is None:
            repository = self.itsView.repository
            repoId, x, x = repository.getSchemaInfo()
            self.client = client = JabberClient(repoId, self, printf)
            worker = JabberWorker(repository)
            worker.start(client)

        reactor.callFromThread(client.connect, self.password, self.useSSL,
                               False, self.itsView)

    def subscribe(self, peerId, name):

        if not self.isLoggedIn():
            raise ValueError, "no connection"

        if '/' not in peerId:
            peerId += "/chandler"

        view = self.itsView
        sidebar = schema.ns('osaf.app', view).sidebarCollection
        share = None

        for collection in sidebar:
            if collection.displayName == name:
                for share in SharedItem(collection).shares:
                    conduit = share.conduit
                    if isinstance(conduit, JabberConduit):
                        if conduit.peerId == peerId:
                            return self.client.sync(share, None, None)

        return self.client.sync(None, peerId, name)

    def sync(self, share):

        if not self.isLoggedIn():
            raise ValueError, "no connection"

        self.client.sync(share)
        return Nil
Esempio n. 8
0
class JabberShare(Share):

    schema.initialValues(
        conduit=lambda self: JabberConduit(
            itsParent=self, peerId=peerId, account=self.account),
        format=lambda self: CloudXMLDiffFormat(itsParent=self))
class AppCollection(ContentCollection):
    """
    AppCollections implement inclusions, exclusions, source,
    and trash along with methods for add and remove.
    """

    __metaclass__ = schema.CollectionClass
    __collection__ = 'set'

    set = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'))

    # must be named 'inclusions' to match ListCollection
    inclusions = inclusions

    # the exclusions used when no exclusions collection is given
    collectionExclusions = schema.Sequence(inverse=ContentItem.excludedBy,
                                           initialValue=[])

    exclusionsCollection = schema.One(inverse=ContentCollection.exclusionsFor,
                                      defaultValue=None)
    trashCollection = schema.One(inverse=ContentCollection.trashFor,
                                 defaultValue=None)

    schema.initialValues(
        # better hope osaf.pim has been loaded!
        trash=lambda self: schema.ns('osaf.pim', self.itsView).trashCollection,
        source=lambda self: None,
    )

    # __collection__ denotes a bi-ref set,
    # therefore it must be added to the copying cloud def for it to be copied.

    schema.addClouds(copying=schema.Cloud(byCloud=[exclusionsCollection],
                                          byRef=[
                                              trashCollection, __collection__,
                                              inclusions, collectionExclusions
                                          ]), )

    # an AppCollection may have another collection for exclusions and that
    # other collection may be the global trash collection. If no collection
    # is specified for exclusions, a local ref collection is used instead.

    def _getExclusions(self):
        exclusions = self.exclusionsCollection
        if exclusions is None:
            exclusions = self.collectionExclusions
        return exclusions

    def _setExclusions(self, exclusions):
        # Typically we will create a collectionExclusions ref collection;
        # however, a collection like 'All' will instead want to use the
        # Trash collection for exclusions
        if exclusions is None:
            self.collectionExclusions = []
            self.exclusionsCollection = None
        else:
            self.exclusionsCollection = exclusions
            if hasattr(self, 'collectionExclusions'):
                del self.collectionExclusions
        self._updateCollection()

    exclusions = property(_getExclusions, _setExclusions)

    # an AppCollection may have another collection for trash. If no
    # collection is given for trash, the collection's exclusions is used
    # instead following the logic above.

    def _getTrash(self):
        trash = self.trashCollection
        if trash is None:
            trash = self.exclusions
        return trash

    def _setTrash(self, trash):
        """
        In general trash should only be the well known Trash
        collection or None. None indicates that this collection does
        not participate in Trash-based activities.

        The special value of Default for trash is only a sentinel to
        let us know that nothing has been passed in and that the
        default trash should be looked up in osaf.pim. During parcel
        loading, this allows us to pass the trash into the constructor
        and avoid trying to look it up in osaf.pim while osaf.pim is
        being loaded.
        """
        # You can designate a certain ListCollection to be used for this
        # collection's trash; in this case, an additional DifferenceCollection
        # will be created to remove any trash items from this collection. Any
        # collections which share a trash get the following benefits:
        # - Adding an item to the trash will make the item disappear from
        #   collections sharing that trash collection
        # - When an item is removed from a collection, it will automatically
        #   be moved to the trash if it doesn't appear in any collection which
        #   shares that trash
        self.trashCollection = trash
        self._updateCollection()

    trash = property(_getTrash, _setTrash)

    def _getSource(self):
        return self.__source

    def _setSource(self, source):
        if hasattr(self, 'source'):
            self.__source = source
            self._updateCollection()
        else:
            self.__source = source

    source = property(_getSource, _setSource)

    def add(self, item):
        """
        Add an item to the collection.
        """
        self.inclusions.add(item)

        exclusions = self.exclusions
        if item in exclusions:
            exclusions.remove(item)

        # If a trash is associated with this collection, remove the item
        # from the trash.  This has the additional benefit of having the item
        # reappear in any collection which has the item in its inclusions

        trash = self.trash
        if trash is not None and item in trash:
            trash.remove(item)

    def remove(self, item):
        """
        Remove an item from the collection.
        """

        isDeleting = item.isDeleting()

        # never add occurrences to the trash or exclusions lists, bug 10777
        isOccurrence = item.itsRefs.get('inheritFrom') is not None

        # adding to exclusions before determining if the item should be added to
        # the trash was a problem at one point (bug 4551), but since the mine/
        # not-mine mechanism changed, this doesn't seem to be a problem anymore,
        # and removing from a mine collection was actually misbehaving if the
        # test was done first, so now logic for moving to the trash has moved
        # back to after addition to exclusions and removal from inclusions.
        if not isDeleting and not isOccurrence:
            self.exclusions.add(item)

        if item in self.inclusions:
            self.inclusions.remove(item)

        trash = self.trash
        pim_ns = schema.ns('osaf.pim', self.itsView)

        if not (isDeleting or trash is None or isOccurrence):
            if isinstance(trash, ContentCollection):
                for collection in itertools.chain(trash.trashFor,
                                                  [pim_ns.allCollection]):
                    # allCollection isn't in trash.trashFor, but needs to be
                    # considered
                    if collection is not self and item in collection:
                        # it exists somewhere else, definitely don't add
                        # to trash
                        break
                else:
                    # we couldn't find it anywhere else, so it goes in the trash
                    trash.add(item)

    def __setup__(self):
        # Ensure that the collection is properly set up
        self._updateCollection()

    def _updateCollection(self):
        if not hasattr(self, 'source'):
            # Can't initialize the collection until the source is known
            return

        exclusions = self.exclusionsCollection
        if exclusions is None:
            exclusions = (self, 'collectionExclusions')

        innerSource = (self, 'inclusions')
        if self.source is not None:
            innerSource = Union(self.source, innerSource)

        set = Difference(innerSource, exclusions)
        trash = self.trashCollection
        if trash is not None:
            set = Difference(set, trash)
        setattr(self, self.__collection__, set)

    def withoutTrash(self, copy=True):
        """
        Pull out the non-trash part of AppCollection.

        Smart collections are 'special' - they almost always include
        the trash as a part of their structure on the _right side of
        their Difference set. This means that when they are hooked
        into a larger collection tree, they need to only give out
        the _left side, which has no trash.
        """

        if self.trash is schema.ns('osaf.pim', self.itsView).trashCollection:
            left = getattr(self, self.__collection__)._left
            if copy:
                return left.copy(self.itsUUID)
            return left

        return self
class MethodIndexDefinition(IndexDefinition):
    """
    A class that allows you to build indexes based on comparing computed
    attributes (i.e. ones that aren't stored in the repository directly).
    Note that this class reinterprets the 'attributes' attribute of
    IndexDefinition: this is now the attributes to _monitor_ (i.e. the ones
    that trigger recomputing the index).

    @cvar findValuePairs: The pairs you want instances to pass to
                          C{IndexDefinition.findValues()} when the index
                          is asked to compare two UUIDs.
    
    @type findValuePairs: C{tuple}
    """
    findValuePairs = ()

    def makeIndexOn(self, collection, kind=None):
        """ Create the index we describe on this collection """

        monitoredAttributes = (self.attributes or [])

        # We need to include inheritFrom in the attributes we monitor,
        # else (especially at Occurrence creation time) items don't get
        # re-indexed properly when they inherit attribute values from
        # their "rich relatives" (ovaltofu's term).
        if not 'inheritFrom' in monitoredAttributes:
            monitoredAttributes = (
                'inheritFrom', ) + tuple(monitoredAttributes)

        collection.addIndex(self.itsName,
                            'method',
                            method=(self, 'compare'),
                            monitor=monitoredAttributes,
                            kind=kind)

    def compare(self, index, u1, u2, vals):
        """
        Compare two items, given their UUIDs. This method fetches (using
        findValues() on the item UUIDs) the pairs specified in the
        C{findValuePairs} (class) variable.
        """
        if not self.findValuePairs:
            raise TypeError(
                "pim.MethodIndexDefinition is an abstract type; use a " \
                "subtype that sets findValuePairs"
            )
        view = self.itsView
        if u1 in vals:
            v1 = vals[u1]
        else:
            v1 = view.findInheritedValues(u1, *self.findValuePairs)
        if u2 in vals:
            v2 = vals[u2]
        else:
            v2 = view.findInheritedValues(u1, *self.findValuePairs)
        return cmp(v1, v2)

    def compare_init(self, index, u, vals):
        return self.itsView.findInheritedValues(u, *self.findValuePairs)

    schema.initialValues(
        # Make the attributes we monitor be the same as the ones we'll
        # fetch in findValues().
        attributes=lambda self:
        [tuple[0] for tuple in type(self).findValuePairs])
Esempio n. 11
0
class Note(items.ContentItem):

    icalUID = schema.One(
        schema.Text,
        doc="iCalendar uses arbitrary strings for UIDs, not UUIDs.  We can "
        "set UID to a string representation of UUID, but we need to be "
        "able to import iCalendar events with arbitrary UIDs.")

    icalendarExtra = schema.One(
        schema.Text,
        defaultValue='',
        doc="Fragment of an icalendar file containing unrecognized data "
        "in iCalendar format.")

    # icalendarProperties and icalendarParameters are preserved just until
    # the old XML + ics dual fork code goes away, the attribute that's used
    # by current code is icalendarExtra
    icalendarProperties = schema.Mapping(
        schema.Text,
        defaultValue=Empty,
        doc="Original icalendar property name/value pairs not understood "
        "by Chandler.  Subcomponents (notably VALARMS) aren't stored.")

    icalendarParameters = schema.Mapping(
        schema.Text,
        defaultValue=Empty,
        doc="property name/parameter pairs for parameters not understood by "
        "Chandler.  The parameter value is the concatenation of "
        "paramater key/value pairs, separated by semi-colons, like the "
        "iCalendar serialization of those parameters")

    schema.addClouds(sharing=schema.Cloud(literal=[icalUID], ))

    schema.initialValues(icalUID=lambda self: unicode(self.itsUUID))

    def InitOutgoingAttributes(self):
        """ Init any attributes on ourself that are appropriate for
        a new outgoing item.
        """

        super(Note, self).InitOutgoingAttributes()
        self.processingStatus = 'processing'

    def ExportItemData(self, clipboardHandler):
        # Create data for this kind of item in the clipboard handler
        # The data is used for Drag and Drop or Cut and Paste
        super(Note, self).ExportItemData(clipboardHandler)

        # Let the clipboard handler know we've got a Note to export
        clipboardHandler.ExportItemFormat(self, 'Note')

    def addDisplayDates(self, dates, now):
        super(Note, self).addDisplayDates(dates, now)

        for stampObject in Stamp(self).stamps:
            method = getattr(stampObject, 'addDisplayDates',
                             lambda _, __: None)
            method(dates, now)

    def addDisplayWhos(self, whos):
        super(Note, self).addDisplayWhos(whos)

        for stampObject in Stamp(self).stamps:
            method = getattr(stampObject, 'addDisplayWhos', lambda _: None)
            method(whos)

        from osaf.communicationstatus import CommunicationStatus
        CommunicationStatus(self).addDisplayWhos(whos)
Esempio n. 12
0
class Triageable(Remindable):
    """ An item with triage status, in all its flavors"""

    # Notes:
    #
    # - There are two sets of triage status attributes:
    #   - 'triageStatus' is used to set (and is changed by) the dashboard
    #     column cell and the detail view markup-bar button. It's also used
    #     for ordering in the dashboard unless there's this:
    #   - 'sectionTriageStatus' overrides triageStatus for sorting, if it
    #     exists; the 'Triage' toolbar button removes the sectionTriageStatus
    #     attributes to force re-sorting by triageStatus alone.
    #
    # - Each actually consists of two attributes: the actual status attribute,
    #   and a timestamp (in UTC) that says how recently the attribute was
    #   changed; it's used for subsorting in each triage status section in the
    #   dashboard.
    #
    # - These attributes are generally set only by the setTriageStatus method,
    #   and read using the Calculated properties.
    #   which takes care of updating the timestamp attributes appropriately.
    #   The raw attributes are directly accessible if you want to avoid the
    #   magic (attn: sharing and recurrence :-) )

    _triageStatus = schema.One(TriageEnum,
                               defaultValue=TriageEnum.now,
                               indexed=True)
    _sectionTriageStatus = schema.One(TriageEnum)
    _triageStatusChanged = schema.One(schema.Float, defaultValue=None)
    _sectionTriageStatusChanged = schema.One(schema.Float)

    # Should we autotriage when the user changes a date or when an alarm fires?
    # Normally yes, so starts True; no once the user has manually set triage
    # status.
    # @@@ currently, never reset to yes; possible a 1.0 task will do this when
    # triage is set (either manually, or by the user) to the value it would have
    # if autotriaged... or something like that.
    doAutoTriageOnDateChange = schema.One(schema.Boolean, defaultValue=True)

    schema.addClouds(
        sharing=schema.Cloud(
            # To avoid compatibility issues with old shares, I didn't add
            # doAutoTriageOnDateChange to the sharing cloud -- this is all
            # going away soon anyway...
            literal=[_triageStatus, _triageStatusChanged], ), )

    schema.initialValues(_triageStatusChanged=lambda self: self.
                         makeTriageStatusChangedTime(self.itsView))

    @staticmethod
    def makeTriageStatusChangedTime(view, when=None):
        # get a float representation of a time from 'when' (or the current
        # time if when is None or not passed)
        if isinstance(when, float):
            pass  # nothing to do
        elif isinstance(when, datetime):
            # (mktime wants local time, so make sure 'when' is
            # in the local timezone)
            when = -time.mktime(
                when.astimezone(view.tzinfo.default).timetuple())
        else:
            when = -time.time()
        return when

    def setTriageStatus(self,
                        newStatus=None,
                        when=None,
                        pin=False,
                        popToNow=False,
                        force=False):
        """
        Set triageStatus to this value, and triageStatusChanged
        to 'when' if specified (or the current time if not).
        
        Newstatus can be 'auto' to autotriage this item based on its 
        inherent times (eg, startTime for an event, or alarm time if the
        item has one).
        
        If pin, save the existing triageStatus/triageStatusChanged
        in _sectionTriageStatus & _sectionTriageStatusChanged, which will
        have the effect of keeping the item in place in the dashboard until
        the next purge. (If there's already a _sectionTriageStatus value,
        don't overwrite it, unless force is True.)
        
        If popToNow, use _sectionTriageStatus/_sectionTriageStatusChanged
        to pop the item to the top of the Now section (again, only if force
        if the item already has section status).
        """
        if (hasattr(self, 'proxiedItem')
                and getattr(self.proxiedItem, 'inheritFrom', None) and
            (not self.changing or self.changing.__name__ != 'CHANGE_THIS')):
            # setTriageStatus on a proxy should do nothing, unless it's
            # changing a single occurrence
            return
        # Don't autotriage unless the flag says we should. (We keep going if we
        # also want to pop-to-now, though)
        if newStatus == 'auto' and not popToNow \
           and not self.doAutoTriageOnDateChange:
            #from osaf.framework.blocks.Block import debugName
            #logger.debug("Not Autotriaging %s", debugName(self))
            return

        # Don't reindex or notify until we're done with these changes
        view = self.itsView
        with view.observersDeferred():
            with view.reindexingDeferred():
                # Manipulate section status if necessary
                if pin:
                    if not hasattr(self, '_sectionTriageStatus'):
                        self.__setTriageAttributes(self._triageStatus,
                                                   self._triageStatusChanged,
                                                   True, force)
                elif popToNow:
                    #from osaf.framework.blocks.Block import debugName
                    #logger.debug("Popping %s to Now", debugName(self))
                    self.__setTriageAttributes(TriageEnum.now, None, True,
                                               force)
                else:
                    # We're not pinning in place, and we're not popping to Now.
                    # Get rid of any existing section triage status
                    if self.hasLocalAttributeValue('_sectionTriageStatus'):
                        del self._sectionTriageStatus
                    if self.hasLocalAttributeValue(
                            '_sectionTriageStatusChanged'):
                        del self._sectionTriageStatusChanged

                # Autotriage if we're supposed to.
                if newStatus == 'auto':
                    # Skip out if we're not supposed to.
                    if not self.doAutoTriageOnDateChange:
                        return

                    # Give our stamps a chance to autotriage
                    newStatus = None
                    from stamping import Stamp
                    for stampObject in Stamp(self).stamps:
                        # If the stamp object has an autoTriage method, and
                        # returns True when we call it, we're done.
                        method = getattr(type(stampObject), 'autoTriage', None)
                        if method is not None:
                            newStatus = method(stampObject)
                            if newStatus is not None:
                                if isinstance(newStatus, tuple):
                                    # The stamp specified a time too - note it.
                                    (newStatus, when) = newStatus
                                else:
                                    when = None
                                break

                    if newStatus is None:
                        # The stamps didn't do it; put ourself in Later if we
                        # have a future reminder. Otherwise, leave things be.
                        reminder = self.getUserReminder()
                        if reminder is not None \
                           and reminder.nextPoll is not None \
                           and reminder.nextPoll != reminder.farFuture \
                           and reminder.nextPoll > datetime.now(view.tzinfo.default):
                            #from osaf.framework.blocks.Block import debugName
                            #logger.debug("Autotriaging %s to LATER for reminder",
                            #debugName(self))
                            newStatus = TriageEnum.later

                # If we were given, or calculated, a triage status, set it.
                if newStatus is not None:
                    self.__setTriageAttributes(newStatus, when, False, True)

    triageStatus = schema.Calculated(TriageEnum,
                                     fget=lambda self: self._triageStatus,
                                     basedOn=(_triageStatus, ))

    triageStatusChanged = schema.Calculated(
        schema.Float,
        fget=lambda self: self._triageStatusChanged,
        basedOn=(_triageStatusChanged, ))

    sectionTriageStatus = schema.Calculated(
        TriageEnum,
        fget=lambda self: getattr(self, '_sectionTriageStatus', self.
                                  _triageStatus),
        basedOn=(_sectionTriageStatus, _triageStatus),
        doc="Allow _sectionTriageStatus to override triageStatus")

    def __setTriageAttributes(self, newStatus, when, section, force):
        """
        Common code for setTriageStatus and setSectionTriageStatus
        """
        # Don't if we already have this attribute pair, unless we're forcing.
        tsAttr = '_sectionTriageStatus' if section else '_triageStatus'
        if not force and hasattr(self, tsAttr):
            return

        # Don't if we're in the middle of sharing...
        # @@@ I'm not sure if this is still necessary...
        if getattr(self, '_share_importing', False):
            return

        tscValue = Triageable.makeTriageStatusChangedTime(self.itsView, when)
        setattr(self, tsAttr, newStatus)
        tscAttr = '_sectionTriageStatusChanged' if section else '_triageStatusChanged'
        setattr(self, tscAttr, tscValue)

    def copyTriageStatusFrom(self, item):
        self._triageStatus = item._triageStatus
        if item._triageStatusChanged is not None:
            self._triageStatusChanged = item._triageStatusChanged
        if hasattr(item, '_sectionTriageStatus'):
            self._sectionTriageStatus = item._sectionTriageStatus
            stsc = getattr(item, '_sectionTriageStatusChanged', None)
            if stsc is not None:
                self._sectionTriageStatusChanged = stsc
        elif hasattr(self, '_sectionTriageStatus'):
            del self._sectionTriageStatus
            if hasattr(self, '_sectionTriageStatusChanged'):
                del self._sectionTriageStatusChanged
        self.doAutoTriageOnDateChange = item.doAutoTriageOnDateChange

    def purgeSectionTriageStatus(self):
        """ 
        If this item has section status that's overriding its triage
        status, purge it. 
        """
        for attr in ('_sectionTriageStatus', '_sectionTriageStatusChanged'):
            inheritedFrom = getattr(self, 'inheritFrom', None)
            if inheritedFrom is not None and hasattr(inheritedFrom, attr):
                delattr(inheritedFrom, attr)
            if hasattr(self, attr):
                delattr(self, attr)

    def resetAutoTriageOnDateChange(self):
        """
        The user changed triage status. Disable certain future automatic
        triaging
        
        @@@ Future: ... unless this change is to the status that the
        item would be triaged to if we autotriaged it now, in which
        case we re-enable future autotriaging.
        """
        self.doAutoTriageOnDateChange = False

    def reminderFired(self, reminder, when):
        """
        Override of C{Remindable.reminderFired}: sets triageStatus
        to now as of the time the reminder was due.
        """
        pending = super(Triageable, self).reminderFired(reminder, when)

        self.setTriageStatus(TriageEnum.now, when=when)
        self.resetAutoTriageOnDateChange()
        return pending

    if __debug__:

        def triageState(self):
            """ 
            For debugging, collect the triage status variables in a tuple 
            """
            def changedToDate(c):
                return None if c is None else time.asctime(time.gmtime(-c))

            return (getattr(self, '_triageStatus', None),
                    changedToDate(getattr(self, '_triageStatusChanged', None)),
                    getattr(self, '_sectionTriageStatus', None),
                    changedToDate(
                        getattr(self, '_sectionTriageStatusChanged', None)),
                    getattr(self, 'doAutoTriageOnDateChange', None))
Esempio n. 13
0
class Script(pim.ContentItem):
    """ Persistent Script Item, to be executed. """
    lastRan = schema.One(schema.DateTime)
    fkey = schema.One(schema.Text, initialValue=u'')
    test = schema.One(schema.Boolean, initialValue=False)

    filePath = schema.One(schema.Text, initialValue=u'')
    lastSync = schema.One(schema.DateTime)

    schema.initialValues(
        private=lambda self: False,  # can share scripts
        displayName=lambda self:
        # XXX check if itsName is a UUID?
        unicode(self.itsName) if self.itsName else messages.UNTITLED,
    )

    def __setup__(self):
        self.lastRan = self.lastSync = datetime.now()

    def execute(self):
        self.sync_file_with_model()
        self.lastRan = datetime.now()

        # this is a nasty hack to import from proxy.py.
        # This is the only way that scripts know about app_ns
        # and other chandler-specific proxies
        from proxy import app_ns
        run_script_with_symbols(self.body,
                                fileName=self.filePath,
                                builtIns=dict(app_ns=app_ns))

    def set_body_quietly(self, newValue):
        if newValue != self.body:
            oldQuiet = getattr(self, '_change_quietly', False)
            self._change_quietly = True
            self.body = newValue
            self._change_quietly = oldQuiet

    @schema.observer(pim.ContentItem.body)
    def onBodyChanged(self, op, name):
        self.model_data_changed()

    def model_data_changed(self):
        if self.filePath and not getattr(self, '_change_quietly', False):
            self.modelModTime = datetime.now()

    def sync_file_with_model(self, preferFile=False):
        """
        Synchronize file and model - which ever is latest wins, with
        conflict dialogs possible if both have changed since the last sync.
        """
        if self.filePath and not getattr(self, '_change_quietly', False):
            writeFile = False
            # make sure we have a model modification time
            if not hasattr(self, 'modelModTime'):
                self.modelModTime = self.lastSync
            # get modification date for the file
            try:
                fileModTime = datetime.fromtimestamp(os.stat(self.filePath)[8])
            except OSError:
                fileModTime = self.lastSync
                writeFile = True
            if preferFile or fileModTime > self.modelModTime:
                self.set_body_quietly(self.file_contents(self.filePath))
            elif writeFile or fileModTime < self.modelModTime:
                # model is newer
                if fileModTime > self.lastSync:
                    msg = _(
                        u"The file associated with this script has changes that are older than your recent edits.\n\nDo you want to overwrite the older changes?"
                    )
                    caption = _(u"Overwrite Script File")
                    if wx.MessageBox(msg,
                                     caption,
                                     style=wx.YES_NO,
                                     parent=wx.GetApp().mainFrame) == wx.NO:
                        return
                self.write_file(self.body)
                fileModTime = datetime.fromtimestamp(os.stat(self.filePath)[8])
            # now the file and model match
            self.lastSync = self.modelModTime = fileModTime

    def file_contents(self, filePath):
        """
        return the contents of our script file
        """
        scriptFile = open(filePath, 'rt')
        try:
            scriptText = scriptFile.read(-1)
        finally:
            scriptFile.close()
        return scriptText

    def write_file(self, scriptText):
        """
        write the contents of our script file into the file
        """
        scriptFile = open(self.filePath, 'wt')
        try:
            scriptText = scriptFile.write(scriptText)
        finally:
            scriptFile.close()

    def set_file(self, fileName, siblingPath):
        #Convert fileName to utf8 encoding
        #to bytes to prevent the join function from trying to downcast
        #the unicode fileName to ascii

        if isinstance(fileName, unicode):
            fileName = fileName.encode('utf8')

        #Convert the filePath bytes to unicode for storage
        filePath = unicode(os.path.join(os.path.dirname(siblingPath), \
                           fileName), sys.getfilesystemencoding())
        self.body = self.file_contents(filePath)
        self.filePath = filePath
Esempio n. 14
0
class TimeZoneInfo(schema.Item):
    """
    Item that persists:
     - A schema.TimeZone attribute that synchronizes
       itself with PyICU's default settings.
     - A list of "well-known" timezone names.
    """

    default = schema.One(schema.TimeZone)

    # List of well-known time zones (for populating drop-downs).
    # [i18n] Since ICU doesn't suitably localize strings like 'US/Pacific',
    # we'll have to provide our own translations.
    wellKnownIDs = schema.Sequence(schema.Text, )

    schema.initialValues(default=lambda self: self.itsView.tzinfo.floating)

    # Observe changes to 'default'.
    # When the view's default timezone changes via another route such as
    # refresh(), ontzchange is invoked by the repository
    @schema.observer(default)
    def onDefaultChanged(self, op, name):

        # Make sure that the view's default timezone is synched with ours
        view = self.itsView
        default = self.default

        # only set the view's default timezone if timezones are used
        if default is not None and default != view.tzinfo.floating:
            assert view.tzinfo.ontzchange is ontzchange
            view.tzinfo.setDefault(default)  # --> ontzchange
        else:
            self.default = self.canonicalTimeZone(default)

    @classmethod
    def get(cls, view):
        """
        Return the default C{TimeZoneInfo} instance, which
        automatically syncs with the view's default; i.e. if you
        assign an ICUtzinfo to C{TimeZoneInfo.get().default},
        this will be stored as the view's default time zone.
        """

        return schema.ns(__name__, view).defaultInfo

    def canonicalTimeZone(self, tzinfo):
        """
        This returns an ICUtzinfo that's equivalent to the passed-in
        tzinfo, to prevent duplicates (like 'PST' and 'US/Pacific'
        from appearing in timezone pickers).

        A side-effect is that if a previously unseen tzinfo is
        passed in, it will be added to the receiver's wellKnownIDs.
        """
        view = self.itsView

        if tzinfo is None or tzinfo == view.tzinfo.floating:
            result = view.tzinfo.floating

        else:
            result = None

            if tzinfo.tzid in self.wellKnownIDs:
                result = tzinfo
            else:
                for equivName in equivalentTZIDs(tzinfo):
                    if equivName in self.wellKnownIDs:
                        result = view.tzinfo.getInstance(equivName)
                        break

            if result is None and tzinfo is not None and tzinfo.tzid in olson_tzids:
                self.wellKnownIDs.append(unicode(tzinfo.tzid))
                result = tzinfo

        return result

    def iterTimeZones(self, withFloating=True):
        """
        A generator for all the well-known ICUtzinfo objects. Each
        generated value is a tuple of the form (display name, ICUtzinfo),
        where 'display name' is a suitably localized unicode string.
        """

        view = self.itsView
        floating = view.tzinfo.floating

        for name in self.wellKnownIDs:
            tzinfo = view.tzinfo.getInstance(name)

            if tzinfo != floating:
                yield (ChandlerMessageFactory(name), tzinfo)

        if withFloating:
            # L10N: Entry in the 'timezone' drop-down menu in the detail view
            # L10N: when an event has no time zone (also known as "Floating"
            # L10N: time). In English, this is translated as "None", but I
            # L10N: didn't want to use "None" in a msgid because that could
            # L10N: be used in other context (e.g. "alarm" dropdown).
            _ = ChandlerMessageFactory  # we want this translated
            yield _(u"None (timezone)"), floating
Esempio n. 15
0
class MailAccount(Account):

    smtp = schema.One(schema.Item, inverse=schema.Sequence())
    imap = schema.One(schema.Item, inverse=schema.Sequence())

    schema.initialValues(
        protocol=lambda self: 'mail',
        userid=lambda self: self.imap.username,
        server=lambda self: self.imap.host,
    )

    def isLoggedIn(self):

        return self.client is not None

    def login(self, printf, autoLogin=False):

        client = self.client

        if client is None:
            repository = self.itsView.repository
            repoId, x, x = repository.getSchemaInfo()
            self.client = client = MailClient(repoId, self, printf)
            worker = MailWorker(repository)
            worker.start(client)

    def send(self, peerId, name):

        if not self.isLoggedIn():
            raise ValueError, "no mail client"

        view = self.itsView
        sidebar = schema.ns('osaf.app', view).sidebarCollection

        for collection in sidebar:
            if collection.displayName == name:
                for share in SharedItem(collection).shares:
                    conduit = share.conduit
                    if isinstance(conduit, MailConduit):
                        if conduit.peerId == peerId:
                            return self.client.send(share, None, None, 'sync')

        return self.client.send(None, peerId, name, 'send')

    def check(self, peerId, name):

        if not self.isLoggedIn():
            raise ValueError, "no mail client"

        self.client.check(None, peerId, name, 'sync')

    def sync(self, share):

        if not self.isLoggedIn():
            raise ValueError, "no mail client"

        if share.ackPending:
            self.client.check(share, None, None, 'receipt')

        self.client.check(share, None, None, 'sync')
        self.client.send(share, None, None, 'sync')

        return Nil
Esempio n. 16
0
class FeedItem(pim.Note):
    """
    This class implements a feed channel item that is visualized
    in the summary and detail views.
    """
    #
    # FeedItem repository interface
    #
    link = schema.One(schema.URL, initialValue=None)
    category = schema.One(schema.Text, indexed=True)
    author = schema.One(schema.Text, indexed=True)
    date = schema.One(schema.DateTime)
    channel = schema.One(FeedChannel)
    content = schema.One(schema.Lob, indexed=True)
    updated = schema.One(schema.Boolean)

    @apply
    def body():
        def fget(self):
            return self.content
        def fset(self, value):
            self.content = value
        return property(fget, fset)

    schema.addClouds(
        sharing = schema.Cloud(
            literal = [link, category, author, date]
        )
    )

    schema.initialValues(
        displayName = lambda self: _(u"No Title")
    )

    def _compareLink(self, other):
        """
        This method compares two feed items.
        """
        return cmp(str(self.link).lower(), str(other.link).lower())

    def refresh(self, data):
        """
        This method updates a feed item content.
        """
        # fill in the item
        attrs = {"title":"displayName"}
        setAttributes(self, data, attrs)

        attrs = ["link", "category", "author"]
        # @@@MOR attrs = ["creator", "link", "category"]
        setAttributes(self, data, attrs)

        content = data.get("content")

        # Use the "content" info first, falling back to what"s in "description"
        if content:
            content = content[0]["value"]
        else:
            content = data.get("description")

        if content:
            self.content = self.getAttributeAspect("content", "type").makeValue(content, indexed=True)

        if "date" in data:
            self.date = date_parse(self.itsView, str(data.date))
        else:
            # No date was available in the feed, so assign it "now"
            self.date = datetime.now(self.itsView.tzinfo.default)

    @schema.observer(author)
    def onAuthorChange(self, op, attr):
        self.updateDisplayWho(op, attr)
    
    def addDisplayWhos(self, whos):
        super(FeedItem, self).addDisplayWhos(whos)
        author = getattr(self, 'author', None)
        if author is not None:
            whos.append((10, author, 'author'))