示例#1
0
class Emojiable(Entity):
    def __init__(self, **kwargs):
        super(Emojiable, self).__init__(**kwargs)
        self.emojis = OOBTree()
        self.users_emoji = OOBTree()

    def add_emoji(self, emoji, user):
        user_oid = get_oid(user)
        current_emoji = self.get_user_emoji(user)
        if current_emoji:
            self.remove_emoji(current_emoji, user)

        if emoji:
            self.emojis.setdefault(emoji, PersistentList())
            self.emojis[emoji].append(user_oid)
            self.users_emoji[user_oid] = emoji

    def remove_emoji(self, emoji, user):
        user_oid = get_oid(user)
        if emoji in self.emojis and \
           user_oid in self.emojis[emoji]:
            self.emojis[emoji].remove(user_oid)
            self.users_emoji.pop(user_oid)

    def get_user_emoji(self, user):
        user_oid = get_oid(user)
        return self.users_emoji.get(user_oid, None)

    def can_add_reaction(self, user, process):
        return False
示例#2
0
文件: core.py 项目: ecreall/nova-ideo
class Emojiable(Entity):

    def __init__(self, **kwargs):
        super(Emojiable, self).__init__(**kwargs)
        self.emojis = OOBTree()
        self.users_emoji = OOBTree()

    def add_emoji(self, emoji, user):
        user_oid = get_oid(user)
        current_emoji = self.get_user_emoji(user)
        if current_emoji:
            self.remove_emoji(current_emoji, user)
        
        if emoji:
            self.emojis.setdefault(emoji, PersistentList())
            self.emojis[emoji].append(user_oid) 
            self.users_emoji[user_oid] = emoji

    def remove_emoji(self, emoji, user):
        user_oid = get_oid(user)
        if emoji in self.emojis and \
           user_oid in self.emojis[emoji]:
            self.emojis[emoji].remove(user_oid)
            self.users_emoji.pop(user_oid)

    def get_user_emoji(self, user):
        user_oid = get_oid(user)
        return self.users_emoji.get(user_oid, None)

    def can_add_reaction(self, user, process):
        return False
示例#3
0
class RuleSet(Content):
    type_title = _("Ruleset")
    _referenced_questions = frozenset()
    choice_scores = None
    nav_visible = False

    @property
    def referenced_questions(self):
        return self._referenced_questions
    @referenced_questions.setter
    def referenced_questions(self, value):
        self._referenced_questions = frozenset(value)

    def __init__(self, **kw):
        super(RuleSet, self).__init__(**kw)
        self.choice_scores = OOBTree()

    def set_choice_score(self, question, choice, score):
        q_cluster = _question_by_type_or_id(question)
        c_cluster = _choice_by_type_or_id(choice)
        assert isinstance(score, int)
        choices = self.choice_scores.setdefault(q_cluster, OIBTree())
        choices[c_cluster] = score

    def get_choice_score(self, question, choice, default = None):
        q_cluster = _question_by_type_or_id(question)
        c_cluster = _choice_by_type_or_id(choice)
        return self.choice_scores.get(q_cluster, {}).get(c_cluster, default)
示例#4
0
class Users(Folder, JSONRenderable):
    def __init__(self, **kw):
        super().__init__(**kw)
        self.providers = OOBTree()

    def add_provider(self, user, userpayload: dict):
        """
        :param user: A Kedja User object
        :param userpayload: the result from the authomatic login, the user part as a dict
        """
        provider = self.providers.setdefault(userpayload['provider'],
                                             OLBTree())
        provider[userpayload['id']] = user.rid

    def find_providers_user(self, result, default=None):
        """
        :param result: Authomatic login result
        :param default: Return value when not found
        :return: User or default
        """
        user_rid = self.providers.get(result.provider.name,
                                      {}).get(result.user.id, None)
        return self.get_rid_user(user_rid, default)

    def get_rid_user(self, rid, default=None):
        """
        :param rid: Users RID (resource  ID)
        :param default: Return value when not found
        :return: User or default
        """
        root = find_root(self)
        user = root.rid_map.get_resource(rid, default)
        if IUser.providedBy(user):
            return user
        return default
示例#5
0
class SyndicationNotificationTool(Persistent, SimpleItem):
    """
    See INotificationUtility for details
    """

    requeue_limit = 7

    def __init__(self):
        self.queues = OOBTree()
        self.requeue_limit = 7

    def get_queue(self, obj):
        uid = api.content.get_uuid(obj)
        return self.queues[uid]

    def put_notification(self, obj, payload):
        uid = api.content.get_uuid(obj)
        self.queues.setdefault(uid, CompositeQueue()).put(payload)

    def pull_notification(self, obj, index=0):
        uid = api.content.get_uuid(obj)
        # Can raise the following errors, must be handled by caller:
        #   KeyError: if the object has had no notifications queued yet
        #   IndexError: if the requested index isn't present in the queue
        return self.queues[uid].pull(index)

    def requeue_notification(self, obj, payload):
        curr_try = payload.get('retries', 0)
        payload['retries'] = curr_try + 1
        if payload['retries'] <= self.requeue_limit:
            # re-try for a week
            logger.info('Re-queueing %r: %r', obj, payload)
            self.put_notification(obj, payload)
        else:
            logger.warning('Not requeuing (retries exceeded) %r: %r', obj,
                           payload)
示例#6
0
    def __generar_indice(self):
        ''' Genera los pares la lista de pares (palabra, docID) ordenada por palabra
        '''
        pares = []
        indice = OOBTree()

        for doc in self._docs:
            lista_palabras = [
                palabra for palabra in self._docs[doc].split()
                if not palabra in self.stop_words
            ]
            lista_palabras = [
                self.__lematizar_palabra(palabra) for palabra in lista_palabras
            ]

            pares = pares + [(palabra, self._doc_to_docID[doc])
                             for palabra in lista_palabras]
        for par in pares:
            posting = indice.setdefault(par[0], set())
            posting.add(par[1])
        self._indice = indice
示例#7
0
    def index_object(self, documentId, obj, threshold=None):
        # XXX should move to adapter
        at_refs = getattr(obj, 'at_references', None)
        if at_refs is None:
            return False

        unindex = OOBTree()
        found_refs = False
        for ref in at_refs.objectValues():
            found_refs = True
            reftype = ref.relationship
            target = ref.targetUID

            reftype_index = self._index.setdefault(reftype, OOBTree())
            target_index = reftype_index.setdefault(target, IITreeSet())
            target_index.add(documentId)

            reftype_unindex = unindex.setdefault(reftype, OISet())
            reftype_unindex.add(target)

        self._unindex[documentId] = unindex
        if found_refs:
            self._length.change(1)
        return True
示例#8
0
class RedirectionStorage(Persistent):
    """Stores old paths to new paths.

    Note - instead of storing "new paths" it could store object ids or
    similar. In general, there is a many-to-one relationship between
    "old paths" and "new paths". An "old path" points to exactly one
    "new path" (where the object is now to be found), but a "new path"
    can be pointed to by multiple different "old paths" (several objects
    that used to be distinct are now consolidated into one).

    The following tests (see test_storage.py) demonstrate its usage.

        >>> p = RedirectionStorage()

    Add one redirect

        >>> p.has_path('/foo')
        False
        >>> p.add('/foo', '/bar')
        >>> p.has_path('/foo')
        True
        >>> p.get('/foo')
        '/bar'
        >>> p.has_path('/bar')
        False
        >>> p.redirects('/bar')
        ['/foo']

    Note that trailing slashes are ignored:

        >>> p.has_path('/foo/')
        True
        >>> p.get('/foo/')
        '/bar'
        >>> p.redirects('/bar/')
        ['/foo']

    Circular references are ignored

        >>> p.add('/circle', '/circle')
        >>> p.has_path('/circle')
        False
        >>> p.get('/circle', '_marker_')
        '_marker_'
        >>> p.redirects('/circle')
        []

    Add another redirect

        >>> p.has_path('/baz')
        False
        >>> p.add('/baz', '/bar')
        >>> p.has_path('/baz')
        True
        >>> p.get('/baz')
        '/bar'
        >>> sorted(p.redirects('/bar'))
        ['/baz', '/foo']

    Update a redirect

        >>> p.add('/foo', '/quux')
        >>> p.has_path('/foo')
        True
        >>> p.get('/foo')
        '/quux'
        >>> p.redirects('/bar')
        ['/baz']
        >>> p.redirects('/quux')
        ['/foo']

    Remove a redirect

        >>> p.remove('/foo')
        >>> p.has_path('/foo')
        False
        >>> p.get('/foo', default='_notfound_')
        '_notfound_'
        >>> p.redirects('/quux')
        []

    Update a redirect in a chain

        >>> p.add('/fred', '/foo')
        >>> p.get('/fred')
        '/foo'
        >>> sorted(p.redirects('/foo'))
        ['/fred']

        >>> p.add('/fred', '/barney')
        >>> p.get('/fred')
        '/barney'
        >>> sorted(p.redirects('/foo'))
        []
        >>> sorted(p.redirects('/barney'))
        ['/fred']

        >>> p.add('/barney', '/wilma')
        >>> p.get('/fred')
        '/wilma'
        >>> p.get('/barney')
        '/wilma'
        >>> sorted(p.redirects('/wilma'))
        ['/barney', '/fred']
        >>> sorted(p.redirects('/barney'))
        []

    Destroy the target of a redirect

        >>> p.destroy('/wilma')
        >>> p.has_path('/barney')
        False
        >>> p.has_path('/fred')
        False
        >>> p.redirects('/wilma')
        []

    What about three step circular rename ?

    Add first redirect.

        >>> p.add('first', 'second')

    There is only one redirect.

        >>> p.get('first')
        'second'
        >>> p.get('second')
        >>> p.get('third')

    There is one back reference.

        >>> p.redirects('first')
        []
        >>> p.redirects('second')
        ['first']
        >>> p.redirects('third')
        []

    Add second redirect.

        >>> p.add('second', 'third')

    There are now two.

        >>> p.get('first')
        'third'
        >>> p.get('second')
        'third'
        >>> p.get('third')

    There are two back references as well.

        >>> p.redirects('first')
        []
        >>> p.redirects('second')
        []
        >>> p.redirects('third')
        ['first', 'second']

    Add third redirect, CIRCULAR.

        >>> p.add('third', 'first')

    There are still only two redirects.

        >>> p.get('first')
        >>> p.get('second')
        'first'
        >>> p.get('third')
        'first'

    And same for the back references.

        >>> p.redirects('first')
        ['second', 'third']
        >>> p.redirects('second')
        []
        >>> p.redirects('third')
        []

    Cleanup after circular

        >>> p.remove('second')
        >>> p.remove('third')

    We can get an iterator over all existing paths

        >>> iter(p)
        <OO-iterator object at ...>
        >>> sorted(p)
        ['/baz']

    Now add some more

        >>> p.add('/foo', '/bar')
        >>> p.add('/barney', '/wilma')
        >>> sorted(p)
        ['/barney', '/baz', '/foo']
    """

    implements(IRedirectionStorage)

    def __init__(self):
        self._paths = OOBTree()
        self._rpaths = OOBTree()

    def add(self, old_path, new_path):
        old_path = self._canonical(old_path)
        new_path = self._canonical(new_path)

        if old_path == new_path:
            return

        # Forget any existing reverse paths to old_path
        existing_target = self._paths.get(old_path, None)
        if (existing_target is not None) and (existing_target in self._rpaths):
            if len(self._rpaths[existing_target]) == 1:
                del self._rpaths[existing_target]
            else:
                self._rpaths[existing_target].remove(old_path)

        # Update any references that pointed to old_path
        for p in self.redirects(old_path):
            if p != new_path:
                self._paths[p] = new_path
                self._rpaths.setdefault(new_path, OOSet()).insert(p)
            else:
                del self._paths[new_path]

        # Remove reverse paths for old_path
        if old_path in self._rpaths:
            del self._rpaths[old_path]

        self._paths[old_path] = new_path
        self._rpaths.setdefault(new_path, OOSet()).insert(old_path)

    def remove(self, old_path):
        old_path = self._canonical(old_path)
        new_path = self._paths.get(old_path, None)
        if new_path is not None and new_path in self._rpaths:
            if len(self._rpaths[new_path]) == 1:
                del self._rpaths[new_path]
            else:
                self._rpaths[new_path].remove(old_path)
        del self._paths[old_path]

    def destroy(self, new_path):
        new_path = self._canonical(new_path)
        for p in self._rpaths.get(new_path, []):
            if p in self._paths:
                del self._paths[p]
        if new_path in self._rpaths:
            if new_path in self._rpaths:
                del self._rpaths[new_path]

    def has_path(self, old_path):
        old_path = self._canonical(old_path)
        return old_path in self._paths

    def get(self, old_path, default=None):
        old_path = self._canonical(old_path)
        return self._paths.get(old_path, default)

    def redirects(self, new_path):
        new_path = self._canonical(new_path)
        return [a for a in self._rpaths.get(new_path, [])]

    def _canonical(self, path):
        if path.endswith('/'):
            path = path[:-1]
        return path

    def __iter__(self):
        return iter(self._paths)
示例#9
0
文件: storage.py 项目: CGTIC/Plone_SP
class RedirectionStorage(Persistent):
    """Stores old paths to new paths.

    Note - instead of storing "new paths" it could store object ids or
    similar. In general, there is a many-to-one relationship between
    "old paths" and "new paths". An "old path" points to exactly one
    "new path" (where the object is now to be found), but a "new path"
    can be pointed to by multiple different "old paths" (several objects
    that used to be distinct are now consolidated into one).

    The following tests (see test_storage.py) demonstrate its usage.

        >>> p = RedirectionStorage()

    Add one redirect

        >>> p.has_path('/foo')
        False
        >>> p.add('/foo', '/bar')
        >>> p.has_path('/foo')
        True
        >>> p.get('/foo')
        '/bar'
        >>> p.has_path('/bar')
        False
        >>> p.redirects('/bar')
        ['/foo']

    Note that trailing slashes are ignored:

        >>> p.has_path('/foo/')
        True
        >>> p.get('/foo/')
        '/bar'
        >>> p.redirects('/bar/')
        ['/foo']

    Circular references are ignored

        >>> p.add('/circle', '/circle')
        >>> p.has_path('/circle')
        False
        >>> p.get('/circle', '_marker_')
        '_marker_'
        >>> p.redirects('/circle')
        []

    Add another redirect

        >>> p.has_path('/baz')
        False
        >>> p.add('/baz', '/bar')
        >>> p.has_path('/baz')
        True
        >>> p.get('/baz')
        '/bar'
        >>> sorted(p.redirects('/bar'))
        ['/baz', '/foo']

    Update a redirect

        >>> p.add('/foo', '/quux')
        >>> p.has_path('/foo')
        True
        >>> p.get('/foo')
        '/quux'
        >>> p.redirects('/bar')
        ['/baz']
        >>> p.redirects('/quux')
        ['/foo']

    Remove a redirect

        >>> p.remove('/foo')
        >>> p.has_path('/foo')
        False
        >>> p.get('/foo', default='_notfound_')
        '_notfound_'
        >>> p.redirects('/quux')
        []

    Update a redirect in a chain

        >>> p.add('/fred', '/foo')
        >>> p.get('/fred')
        '/foo'
        >>> sorted(p.redirects('/foo'))
        ['/fred']

        >>> p.add('/fred', '/barney')
        >>> p.get('/fred')
        '/barney'
        >>> sorted(p.redirects('/foo'))
        []
        >>> sorted(p.redirects('/barney'))
        ['/fred']

        >>> p.add('/barney', '/wilma')
        >>> p.get('/fred')
        '/wilma'
        >>> p.get('/barney')
        '/wilma'
        >>> sorted(p.redirects('/wilma'))
        ['/barney', '/fred']
        >>> sorted(p.redirects('/barney'))
        []

    Destroy the target of a redirect

        >>> p.destroy('/wilma')
        >>> p.has_path('/barney')
        False
        >>> p.has_path('/fred')
        False
        >>> p.redirects('/wilma')
        []

    What about three step circular rename ?

    Add first redirect.

        >>> p.add('first', 'second')

    There is only one redirect.

        >>> p.get('first')
        'second'
        >>> p.get('second')
        >>> p.get('third')

    There is one back reference.

        >>> p.redirects('first')
        []
        >>> p.redirects('second')
        ['first']
        >>> p.redirects('third')
        []

    Add second redirect.

        >>> p.add('second', 'third')

    There are now two.

        >>> p.get('first')
        'third'
        >>> p.get('second')
        'third'
        >>> p.get('third')

    There are two back references as well.

        >>> p.redirects('first')
        []
        >>> p.redirects('second')
        []
        >>> p.redirects('third')
        ['first', 'second']

    Add third redirect, CIRCULAR.

        >>> p.add('third', 'first')

    There are still only two redirects.

        >>> p.get('first')
        >>> p.get('second')
        'first'
        >>> p.get('third')
        'first'

    And same for the back references.

        >>> p.redirects('first')
        ['second', 'third']
        >>> p.redirects('second')
        []
        >>> p.redirects('third')
        []

    Cleanup after circular

        >>> p.remove('second')
        >>> p.remove('third')

    We can get an iterator over all existing paths

        >>> iter(p)
        <OO-iterator object at ...>
        >>> sorted(p)
        ['/baz']

    Now add some more

        >>> p.add('/foo', '/bar')
        >>> p.add('/barney', '/wilma')
        >>> sorted(p)
        ['/barney', '/baz', '/foo']
    """

    implements(IRedirectionStorage)

    def __init__(self):
        self._paths = OOBTree()
        self._rpaths = OOBTree()

    def add(self, old_path, new_path):
        old_path = self._canonical(old_path)
        new_path = self._canonical(new_path)

        if old_path == new_path:
            return

        # Forget any existing reverse paths to old_path
        existing_target = self._paths.get(old_path, None)
        if (existing_target is not None) and (
            existing_target in self._rpaths):
            if len(self._rpaths[existing_target]) == 1:
                del self._rpaths[existing_target]
            else:
                self._rpaths[existing_target].remove(old_path)

        # Update any references that pointed to old_path
        for p in self.redirects(old_path):
            if p != new_path:
                self._paths[p] = new_path
                self._rpaths.setdefault(new_path, OOSet()).insert(p)
            else:
                del self._paths[new_path]

        # Remove reverse paths for old_path
        if old_path in self._rpaths:
            del self._rpaths[old_path]

        self._paths[old_path] = new_path
        self._rpaths.setdefault(new_path, OOSet()).insert(old_path)

    def remove(self, old_path):
        old_path = self._canonical(old_path)
        new_path = self._paths.get(old_path, None)
        if new_path is not None and new_path in self._rpaths:
            if len(self._rpaths[new_path]) == 1:
                del self._rpaths[new_path]
            else:
                self._rpaths[new_path].remove(old_path)
        del self._paths[old_path]

    def destroy(self, new_path):
        new_path = self._canonical(new_path)
        for p in self._rpaths.get(new_path, []):
            if p in self._paths:
                del self._paths[p]
        if new_path in self._rpaths:
            if new_path in self._rpaths:
                del self._rpaths[new_path]

    def has_path(self, old_path):
        old_path = self._canonical(old_path)
        return old_path in self._paths

    def get(self, old_path, default=None):
        old_path = self._canonical(old_path)
        return self._paths.get(old_path, default)

    def redirects(self, new_path):
        new_path = self._canonical(new_path)
        return [a for a in self._rpaths.get(new_path, [])]

    def _canonical(self, path):
        if path.endswith('/'):
            path = path[:-1]
        return path

    def __iter__(self):
        return iter(self._paths)
class BaseTimeline(Persistent):
    """The base ``ITimeline`` adapter.

    Allows to quickly set up adapters for your content.
    """
    implements(ITimeline)

    def __init__(self):
        self.data = OOBTree()
        self.__parent__ = None
        self.__name__ = None

    def index(self, context, indexes, previous):
        """Returns a dictionary whose keys are the values in ``indexes``
        and the values the index values as of now.

        ``context`` is the adapter context.

        ``previous`` is a dictionary containing the last inserted
        values of the specified ``indexes``.
        """

    def snapshot(self, context, indexes=None, insert=True):
        """Should return a dictionary which has a key
        for every item in ``indexes``,
        and whose values are the correct values for the index
        **at this moment** on the ``context``.
        """
        now = datetime.now()
        previous = {}
        if indexes is None:
            indexes = self.indexes
        for index in indexes:
            previous[index] = self._get_value(index)
        snapshot_ = self.index(context, indexes, previous)
        if insert:
            for index, value in snapshot_.items():
                index = self._get_index(index)
                index[now] = value
        return snapshot_

    def _get_index(self, index):
        return self.data.setdefault(index, OOBTree())

    def _get_value(self, index, limit=None):
        index = self._get_index(index)
        if len(index) == 0:
            return None
        if limit is not None:
            keys = index.keys(max=limit, excludemax=True)
            if len(keys) > 0:
                max_key = keys[-1]
            else:
                return None
        else:
            max_key = index.maxKey()
        return index[max_key]

    def slice(self, from_, to, resolution, indexes=None):
        if indexes is None:
            indexes = self.indexes
        for current, next in api.date.datetimerange(from_, to, resolution):
            result = {}
            for index in indexes:
                try:
                    result[index] = self._get_value(index, next)
                except ValueError:
                    result[index] = None
            yield (current, result)
示例#11
0
class RelationIndex(SimpleItem):
    """Index of relationships between objects."""
    implements(ILimitedResultIndex)

    meta_type = 'RelationIndex'

    manage_options = (
        {'label': 'Settings', 'action': 'manage_main'},
        {'label': 'Browse', 'action': 'manage_browse'},
    )

    manage = manage_main = DTMLFile('dtml/manageRelationIndex', globals())
    manage_main._setName('manage_main')
    manage_browse = DTMLFile('dtml/browseIndex', globals())

    def __init__(self, id, *args, **kw):
        self.id = id
        self.clear()

    def clear(self):
        self._length = Length()
        self._index = OOBTree()
        self._unindex = IOBTree()

    def getId(self):
        return self.id

    def getEntryForObject(self, documentId, default=None):
        return dict(self._unindex.get(documentId, default))

    def getIndexSourceNames(self):
        return self._index.keys()

    def getIndexQueryNames(self):
        return self._index.keys()

    def index_object(self, documentId, obj, threshold=None):
        # XXX should move to adapter
        at_refs = getattr(obj, 'at_references', None)
        if at_refs is None:
            return False

        unindex = OOBTree()
        found_refs = False
        for ref in at_refs.objectValues():
            found_refs = True
            reftype = ref.relationship
            target = ref.targetUID

            reftype_index = self._index.setdefault(reftype, OOBTree())
            target_index = reftype_index.setdefault(target, IITreeSet())
            target_index.add(documentId)

            reftype_unindex = unindex.setdefault(reftype, OISet())
            reftype_unindex.add(target)

        self._unindex[documentId] = unindex
        if found_refs:
            self._length.change(1)
        return True

    def unindex_object(self, documentId):
        entries = self._unindex.get(documentId, {})
        for reftype, targets in entries.items():
            # remove index -> reftype -> source
            reftype_index = self._index.get(reftype)
            if reftype_index is None:
                continue

            for target in targets:
                entry = reftype_index.get(target)
                if entry is None:
                    continue
                entry.remove(documentId)
        self._length.change(-1)
        del self._unindex[documentId]

    def _apply_index(self, request, resultset=None):
        setlist = []
        indices_used = []
        for reltype in self.getIndexSourceNames():
            query = request.get(reltype)
            if query is None:
                continue

            if isinstance(query, str):
                target = query
            else:
                target = IUUID(query)

            indices_used.append(reltype)
            index = self._index[reltype]
            s = index.get(target)
            if s is None:
                continue
            else:
                setlist.append(s)

        if not indices_used:
            return

        if len(setlist) == 1:
            return setlist[0], tuple(indices_used)

        # If we already get a small result set passed in, intersecting
        # the various indexes with it and doing the union later is
        # faster than creating a multiunion first.
        if resultset is not None and len(resultset) < 200:
            smalllist = []
            for s in setlist:
                smalllist.append(intersection(resultset, s))
            r = multiunion(smalllist)
        else:
            r = multiunion(setlist)

        if r is None:
            r = IISet()
        return r, tuple(indices_used)

    def numObjects(self):
        return self._length()
    indexSize = numObjects

    def items(self):
        items = []
        for k, v in self._index.items():
            items.append((k, dict(v.items())))
        return items
class BaseTimeline(Persistent):
    """The base ``ITimeline`` adapter.

    Allows to quickly set up adapters for your content.
    """

    implements(ITimeline)

    def __init__(self):
        self.data = OOBTree()
        self.__parent__ = None
        self.__name__ = None

    def index(self, context, indexes, previous):
        """Returns a dictionary whose keys are the values in ``indexes``
        and the values the index values as of now.

        ``context`` is the adapter context.

        ``previous`` is a dictionary containing the last inserted
        values of the specified ``indexes``.
        """

    def snapshot(self, context, indexes=None, insert=True):
        """Should return a dictionary which has a key
        for every item in ``indexes``,
        and whose values are the correct values for the index
        **at this moment** on the ``context``.
        """
        now = datetime.now()
        previous = {}
        if indexes is None:
            indexes = self.indexes
        for index in indexes:
            previous[index] = self._get_value(index)
        snapshot_ = self.index(context, indexes, previous)
        if insert:
            for index, value in snapshot_.items():
                index = self._get_index(index)
                index[now] = value
        return snapshot_

    def _get_index(self, index):
        return self.data.setdefault(index, OOBTree())

    def _get_value(self, index, limit=None):
        index = self._get_index(index)
        if len(index) == 0:
            return None
        if limit is not None:
            keys = index.keys(max=limit, excludemax=True)
            if len(keys) > 0:
                max_key = keys[-1]
            else:
                return None
        else:
            max_key = index.maxKey()
        return index[max_key]

    def slice(self, from_, to, resolution, indexes=None):
        if indexes is None:
            indexes = self.indexes
        for current, next in api.date.datetimerange(from_, to, resolution):
            result = {}
            for index in indexes:
                try:
                    result[index] = self._get_value(index, next)
                except ValueError:
                    result[index] = None
            yield (current, result)
class PendingList(object):
    """ Implementation of IPendingList

    Set up the pending list
    >>> from Products.listen.content import PendingList
    >>> plist = PendingList()
    
    Add a few pending members
    >>> plist.add('tom')
    >>> plist.add('*****@*****.**')
    >>> plist.add('mikey', time='2006-05-09', pin='4532123')
    >>> sorted(plist.get_user_emails())
    ['*****@*****.**', 'mikey', 'tom']

    The time that we set on mikey should be used instead of the default time
    >>> plist.get_pending_time('mikey')
    '2006-05-09'
    >>> plist.get_user_pin('mikey')
    '4532123'

    Try and add mikey a second time and make sure data is not lost but time is updated
    >>> plist.add('mikey')
    >>> plist.get_user_pin('mikey')
    '4532123'
    >>> plist.get_pending_time('mikey') != '2006-05-09'
    True

    Now let's remove them
    >>> plist.remove('tom')
    >>> plist.remove('*****@*****.**')
    >>> plist.remove('mikey')
    >>> plist.get_user_emails()
    []

    Let's create an item with a post
    >>> plist.add('timmy', post='a new post')
    >>> post = plist.get_posts('timmy')[0]
    >>> post['header']
    {}
    >>> post['body']
    'a new post'

    Verify the id of the post
    >>> post['postid']
    0
    
    Let's add a new post, and verify its id too
    >>> plist.add('timmy', post='hi there')
    >>> newpost = plist.get_posts('timmy')[1]
    >>> newpost['postid']
    1

    Remove the first one
    >>> plist.pop_post('timmy', 0) is not None
    True
    >>> p = plist.get_posts('timmy')[0]
    >>> p['body']
    'hi there'
    >>> p['postid']
    1

    Trying to pop a fake post returns None
    >>> plist.pop_post('timmy', 0) is None
    True
    >>> plist.pop_post('timmy', 17) is None
    True

    """
    implements(IPendingList)

    def __init__(self):
        self.pend = OOBTree()
        self.trust_caller = False

    def add(self, item, **values):
        self.pend.setdefault(item, OOBTree())
        if 'time' not in values:
            if self.trust_caller:
                raise AssertionError("No time passed in: %s" % values)
            values['time'] = DateTime().ISO()
        if 'post' in values:
            post_list = self.pend[item].setdefault('post', IOBTree())
            new_post = values['post']
            if isinstance(new_post, basestring):
                new_post = dict(header={}, body=new_post)

            try:
                nextid = post_list.maxKey() + 1
            except ValueError:
                nextid = 0

            if self.trust_caller:
                assert 'postid' in new_post, new_post
            else:
                new_post['postid'] = nextid
            post_list[new_post['postid']] = new_post
            values.pop('post')
        self.pend[item].update(values)

    def remove(self, item):
        if item in self.pend:
            self.pend.pop(item)

    def pop_post(self, item, postid):
        posts = self.pend[item]['post']
        try:
            return posts.pop(postid)
        except KeyError:
            return None

    def get_posts(self, user_email):
        return list(self.pend.get(user_email, {}).get('post', {}).values())

    def is_pending(self, item):
        return item in self.pend

    def get_user_pin(self, user_email):
        return self.pend.get(user_email, {}).get('pin')

    def get_pending_time(self, user_email):
        return self.pend.get(user_email, {}).get('time')

    def get_user_emails(self):
        return list(self.pend.keys())

    def get_user_name(self, user_email):
        return self.pend.get(user_email, {}).get('user_name')

    def clear(self):
        for email, item in self.pend.items():
            if 'post' in item:
                for post in item['post'].values():
                    for k, v in item.items():
                        if k == 'post': continue
                        post[k] = v
                    post['email'] = email
                    yield post
            else:
                item['email'] = email
                yield item
        self.pend.clear()
示例#14
0
class RedirectionStorage(Persistent):
    """Stores old paths to new paths.

    Note - instead of storing "new paths" it could store object ids or
    similar. In general, there is a many-to-one relationship between
    "old paths" and "new paths". An "old path" points to exactly one
    "new path" (where the object is now to be found), but a "new path"
    can be pointed to by multiple different "old paths" (several objects
    that used to be distinct are now consolidated into one).

    See test_storage.py for demonstrations of its usage.
    """

    def __init__(self):
        self.clear()

    def clear(self):
        # If the data already exists, we could call 'clear' on all BTrees,
        # but making them fresh seems cleaner and faster.
        self._paths = OOBTree()
        self._rpaths = OOBTree()

    def add(self, old_path, new_path, now=None, manual=False):
        old_path = self._canonical(old_path)
        new_path = self._canonical(new_path)

        if old_path == new_path:
            return

        # Forget any existing reverse paths to old_path
        existing_target = self.get(old_path)
        if (existing_target is not None) and (existing_target in self._rpaths):
            # old_path was pointing to existing_target, but now we want it to
            # point to new_path.  So remove the existing reverse path.
            if len(self._rpaths[existing_target]) == 1:
                del self._rpaths[existing_target]
            else:
                self._rpaths[existing_target].remove(old_path)

        if now is None:
            now = DateTime()
        full_value = (new_path, now, manual)

        # Update any references that pointed to old_path
        for p in self.redirects(old_path):
            # p points to old_path, but old_path will point to new_path,
            # so we update p to point to new_path directly.
            if p != new_path:
                old_full_value = self._paths[p]
                if isinstance(old_full_value, tuple):
                    # keep date and manual
                    new_full_value = (
                        new_path,
                        old_full_value[1],
                        old_full_value[2],
                    )
                else:
                    new_full_value = full_value
                self._paths[p] = new_full_value
                self._rpaths.setdefault(new_path, OOSet()).insert(p)
            else:
                # There is an existing redirect from new_path to old_path.
                # We now want to update new_path to point to new_path.
                # This is not useful, so we delete it.
                del self._paths[new_path]

        # Remove reverse paths for old_path.  If old_path was being
        # redirected to, the above code will have updated those redirects,
        # so this reverse redirect info is no longer needed.
        if old_path in self._rpaths:
            del self._rpaths[old_path]

        self._paths[old_path] = full_value
        self._rpaths.setdefault(new_path, OOSet()).insert(old_path)

    __setitem__ = add

    def update(self, info, manual=True):
        # Bulk update information.
        # Calling update will usually be done for manual additions (csv upload).
        now = DateTime()
        for key, value in info.items():
            if isinstance(value, tuple):
                # This is (new path, datetime, manual),
                # where datetime may be None.
                self.add(key, value[0], now=value[1] or now, manual=value[2])
            else:
                self.add(key, value, now=now, manual=manual)

    def remove(self, old_path):
        old_path = self._canonical(old_path)
        new_path = self.get(old_path)
        if new_path is not None and new_path in self._rpaths:
            if len(self._rpaths[new_path]) == 1:
                del self._rpaths[new_path]
            else:
                self._rpaths[new_path].remove(old_path)
        del self._paths[old_path]

    __delitem__ = remove

    def _rebuild(self):
        """Rebuild the information.

        Can be used in migration to initialize the date and manual information.

        For good measure, this also rebuild the _rpaths structure:
        the _paths structure is leading.  For one million paths,
        the _paths rebuilding takes 1 second,
        and the _rpaths an extra 3 seconds.  Seems fine, as this should
        rarely be used.
        """
        now = DateTime()
        self._rpaths = OOBTree()
        for old_path in self._paths:
            new_info = self._paths[old_path]
            if isinstance(new_info, tuple):
                new_path = new_info[0]
            else:
                # Store as tuple: (new_path, date, manual).
                # We cannot know if this was a manual redirect or not.
                # For safety we register this as a manual one.
                new_path = new_info
                new_info = (new_path, now, True)
                self._paths[old_path] = new_info
            self._rpaths.setdefault(new_path, OOSet()).insert(old_path)

        # Look for inconsistenties and fix them:
        # paths that are both in paths and in rpaths.
        bads = [
            new_path for new_path in self._rpaths if new_path in self._paths
        ]
        for new_path in bads:
            newer_path = self._paths[new_path][0]
            for old_path in self._rpaths[new_path]:
                # old_path points to new_path,
                # but new_path points to newer_path.
                # So update old_path to point to newer_path.
                info = self._paths[old_path]
                info = (newer_path, info[1], info[2])
                self._paths[old_path] = info
                self._rpaths[newer_path].insert(old_path)
            # self._rpaths[new_path] is empty now
            del self._rpaths[new_path]

    def destroy(self, new_path):
        new_path = self._canonical(new_path)
        for p in self._rpaths.get(new_path, []):
            if p in self._paths:
                del self._paths[p]
        if new_path in self._rpaths:
            if new_path in self._rpaths:
                del self._rpaths[new_path]

    def has_path(self, old_path):
        old_path = self._canonical(old_path)
        return old_path in self._paths

    __contains__ = has_path

    def get(self, old_path, default=None):
        old_path = self._canonical(old_path)
        new_path = self._paths.get(old_path, default)
        if isinstance(new_path, tuple):
            # (new_path, date, manual)
            return new_path[0]
        return new_path

    def get_full(self, old_path, default=None):
        old_path = self._canonical(old_path)
        new_path = self._paths.get(old_path, default)
        if isinstance(new_path, tuple):
            # (new_path, date, manual)
            return new_path
        return (new_path, None, True)

    def __getitem__(self, old_path):
        result = self.get(old_path, default=_marker)
        if result is _marker:
            raise KeyError(old_path)
        return result

    def redirects(self, new_path):
        new_path = self._canonical(new_path)
        return [a for a in self._rpaths.get(new_path, [])]

    def _canonical(self, path):
        if path.endswith('/'):
            path = path[:-1]
        return path

    def __iter__(self):
        return iter(self._paths)

    def __len__(self):
        return len(self._paths)
示例#15
0
class Question(VersionableEntity, DuplicableEntity,
               SearchableEntity, CorrelableEntity, PresentableEntity,
               ExaminableEntity, Node, Emojiable, SignalableEntity,
               Sustainable, Debatable):
    """Question class"""

    type_title = _('Question')
    icon = 'md md-live-help'
    templates = {'default': 'novaideo:views/templates/question_result.pt',
                 'bloc': 'novaideo:views/templates/question_bloc.pt',
                 'small': 'novaideo:views/templates/small_question_result.pt',
                 'popover': 'novaideo:views/templates/question_popover.pt'}
    template = 'novaideo:views/templates/question_list_element.pt'
    name = renamer()
    author = SharedUniqueProperty('author', 'questions')
    organization = SharedUniqueProperty('organization')
    attached_files = CompositeMultipleProperty('attached_files')
    url_files = CompositeMultipleProperty('url_files')
    related_correlation = SharedUniqueProperty('related_correlation', 'targets')
    answers = CompositeMultipleProperty('answers', 'question')
    answer = SharedUniqueProperty('answer')
    challenge = SharedUniqueProperty('challenge', 'questions')

    def __init__(self, **kwargs):
        super(Question, self).__init__(**kwargs)
        self.set_data(kwargs)
        self.addtoproperty('channels', Channel())
        self.selected_options = OOBTree()
        self.users_options = OOBTree()
        self.urls = PersistentDict({})
        self.len_answers = 0

    @property
    def authors(self):
        return [self.author]

    @property
    def transformed_from(self):
        """Return all related contents"""
        transformed_from = [correlation[1].context for correlation
                            in self.get_related_contents(
                                CorrelationType.solid, ['transformation'])
                            if correlation[1].context]
        return transformed_from[0] if transformed_from else None

    @property
    def relevant_data(self):
        return [getattr(self, 'title', ''),
                getattr(self, 'text', ''),
                ', '.join(self.keywords)]

    def __setattr__(self, name, value):
        super(Question, self).__setattr__(name, value)
        if name == 'author':
            self.init_organization()

    def is_managed(self, root):
        return root.manage_questions

    def update_len_answers(self):
        self.len_answers = len(self.answers)
        return self.len_answers

    def addtoproperty(self, name, value, moving=None):
        super(Question, self).addtoproperty(name, value, moving)
        if name == 'answers':
            self.len_answers += 1

    def delfromproperty(self, name, value, moving=None):
        super(Question, self).delfromproperty(name, value, moving)
        if name == 'answers':
            self.len_answers -= 1

    def init_organization(self):
        if not self.organization:
            organization = getattr(self.author, 'organization', None)
            if organization:
                self.setproperty('organization', organization)

    def presentation_text(self, nb_characters=400):
        return truncate_text(getattr(self, 'text', ''), nb_characters)

    def get_more_contents_criteria(self):
        "return specific query, filter values"
        return None, {
            'metadata_filter': {
                'content_types': ['question'],
                'keywords': list(self.keywords)
            }
        }

    def get_attached_files_data(self):
        return get_files_data(self.attached_files)

    def get_node_descriminator(self):
        return 'question'

    def format(self, request):
        text = getattr(self, 'text', '')
        all_urls, url_files, text_urls, formatted_text = text_urls_format(
            text, request)
        self.urls = PersistentDict(all_urls)
        self.setproperty('url_files', url_files)
        self.formatted_text = formatted_text
        self.formatted_urls = text_urls

    def add_selected_option(self, user, option):
        self.remove_selected_option(user)
        oid = get_oid(user)
        self.selected_options[oid] = option
        self.users_options.setdefault(option, [])
        if option in self.users_options:
            self.users_options[option].append(oid)
        else:
            self.users_options[option] = PersistentList([oid])

    def remove_selected_option(self, user):
        oid = get_oid(user)
        if oid in self.selected_options:
            option = self.selected_options.pop(oid)
            if oid in self.users_options[option]:
                user_options = self.users_options[option]
                user_options.remove(oid)
                self.users_options[option] = PersistentList(
                    list(set(user_options)))

    def get_selected_option(self, user):
        oid = get_oid(user)
        if oid in self.selected_options:
            return self.selected_options.get(oid)

        return None

    def get_user_with_option(self, option):
        options = getattr(self, 'options', [])
        if options and option in self.users_options:
            return self.users_options[option]

        return []
class ObjectRegistry(Persistent):
    """See the objectregistry module docstring, this is deprecated and
    should not be used by new code.
    """
    
    def __init__(self):
        self.__non_unique_key_registry = BTree()
        self.__obr_registry = IOBTree()
        self.__obr_largest_key_ever = -1
        self.__non_unique_keys_for_obj = IOBTree()

    def get_keys_for_object(self, obj):
        if (not hasattr(obj, '_obr_unique_key') or
             not self.__non_unique_keys_for_obj.has_key(obj._obr_unique_key) ):
            return ()
        else:
            return self.__non_unique_keys_for_obj[obj._obr_unique_key]

    def __register_object(self, obj):
        """Should not be called directly, call __get_object_registered
        """
        # important because code below resets things like the non-unique
        # key set
        # 
        assert( not hasattr(obj, '_obr_unique_key') )
        obr_unique_key = get_and_establish_attribute(
                obj, '_obr_unique_key',
                lambda: (0 if len(self.__obr_registry) == 0
                         else max(self.__obr_registry.maxKey() + 1,
                                  self.__obr_largest_key_ever + 1 ) )
                )
        self.__obr_registry[obr_unique_key] = obj
        self.__obr_largest_key_ever = max(obr_unique_key,
                                          self.__obr_largest_key_ever )
        self.__non_unique_keys_for_obj[obr_unique_key] = Set()
        return obr_unique_key
    
    def __get_object_registered(self, obj):
        # the assumption here about order of operations on ternary expressions
        # is that self.__register_object(obj) won't be called unless
        # the ternary condition fails
        result = (obj._obr_unique_key if hasattr(obj, '_obr_unique_key')
                  else self.__register_object(obj) )
        assert( self.__obr_registry.has_key(result) )
        assert( self.__non_unique_keys_for_obj.has_key(result) )
        return result

    def __deregister_object(self, obj):
        obj_key = obj._obr_unique_key
        if self.__non_unique_keys_for_obj.has_key(obj_key):
            assert( len(self.__non_unique_keys_for_obj[obj_key]) == 0 )
            del self.__non_unique_keys_for_obj[obj_key]
        del self.__obr_registry[obj_key]
        delattr(obj, '_obr_unique_key')

    def register_interest_by_non_unique_key(
        self, key, obj, owner):
        obj_key, owner_key = (self.__get_object_registered(obj), 
                              self.__get_object_registered(owner) )
        set_for_key = self.__non_unique_key_registry.setdefault(key, Set())
        set_for_key.insert( (obj_key, owner_key) )
        for obr_unique_key in obj_key, owner_key:
            self.__non_unique_keys_for_obj[obr_unique_key].insert( key )

    def registered_obj_and_owner_per_unique_key(self, key):
        return ( (self.__obr_registry[obj_key], self.__obr_registry[owner_key])
                 for obj_key, owner_key
                 in self.__non_unique_key_registry.get(key,() )
                 ) # end generator expression
            

    def registered_obj_and_owner_per_unique_key_range(self, key_min, key_max):
        return ( (key, (self.__obr_registry[obj_key],
                        self.__obr_registry[owner_key]) ) # end tuple
                 for (key, da_set)
                 in self.__non_unique_key_registry.iteritems(key_min, key_max)
                 for (obj_key, owner_key) in da_set
                 ) # end generator expression
    
    def deregister_interest_by_non_unique_key(self, key, obj, owner):
        obj_key = obj._obr_unique_key
        owner_key = owner._obr_unique_key
        self.__non_unique_key_registry.get(key).remove( (obj_key, owner_key) )

        # this is sort of rediculous counting up all the references for
        # obj and owner and making a deletion decision on that
        # when we could of just kept count them all along
        #
        # And remember, this isn't the count for all references for obj and
        # owner, just all references related to key, very important at the
        # end of this function
        obj_count = 0
        owner_count = 0
        for obj_search, owner_search in \
            self.registered_obj_and_owner_per_unique_key(key):
            obj_search_key = obj_search._obr_unique_key
            owner_search_key = owner_search._obr_unique_key
            if obj_search_key == obj_key: obj_count+=1
            if owner_search_key == owner_key: owner_count+=1
        for count, da_obj_key, da_obj in \
                ( (obj_count, obj_key, obj), (owner_count, owner_key, owner) ):
            if count == 0:
                self.__non_unique_keys_for_obj[da_obj_key].remove(key)
                # very important, the association of da_obj with
                # the key had to have been the only key for us to
                # completely deregister it
                if len(self.__non_unique_keys_for_obj[da_obj_key]) == 0:
                    self.__deregister_object(da_obj)

    def final_deregister_interest_for_obj_non_unique_key(self, key, obj, owner):
        obj_key = obj._obr_unique_key
        owner_key = owner._obr_unique_key
        # this is a rediculous linear implementation, the structure clearly
        # needs to be altered to nested sets instead
        da_set = self.__non_unique_key_registry.get(key)
        # filter by entries with the same object
        da_list  = [ (search_obj_key, search_owner_key)
                     for search_obj_key, search_owner_key in da_set
                     if search_obj_key == obj_key ]
        assert( len(da_list) >= 1 )
        if len(da_list) > 1:
            return False
        else:
            assert(len(da_list) == 1 )
            assert( da_list[0] == (obj_key, owner_key) )
            self.deregister_interest_by_non_unique_key(
                key, obj, owner)
            return True
示例#17
0
class Users(Persistent):
    implements(IUsers)
    data = None

    def __init__(self):
        self.data = OOBTree()
        self.byid = self.data  # b/c
        self.logins = OOBTree()
        self.groups = OOBTree()

    def _convert(self, s):
        if isinstance(s, basestring):
            if not isinstance(s, unicode):
                s = unicode(s, 'utf-8')
        return s

    def _upgrade(self):
        # older revisions of this class used 2 btrees: "bylogin" and
        # "byid", instead of a "data" btree, a "groups" btree, and a
        # "logins" btree; this method upgrades the persistent
        # state of old instances
        if self.data is None:
            self.data = self.byid
            self.logins = OOBTree()
            self.groups = OOBTree()
            for login, info in self.bylogin.items():
                login = self._convert(login)
                userid = self._convert(info['id'])
                self.logins[login] = userid
                groups = info['groups']
                for group in groups:
                    group = self._convert(group)
                    groupset = self.groups.setdefault(group, set())
                    groupset.add(userid)
                    self.groups[group] = groupset
            del self.bylogin

    def get_by_login(self, login):
        # b/c
        return self.get(login=login)

    def get_by_id(self, userid):
        # b/c
        return self.get(userid=userid)

    def get(self, userid=None, login=None):
        self._upgrade()
        if userid is not None and login is not None:
            raise ValueError('Only one of userid or login may be supplied')

        if userid is not None:
            userid = self._convert(userid)
            return self.data.get(userid)

        if login is not None:
            login = self._convert(login)
            userid = self.logins.get(login)
            if userid is None:
                return None
            return self.data.get(userid)

        raise ValueError('Either userid or login must be supplied')

    def add(self, userid, login, cleartext_password, groups=None):
        self._upgrade()
        salt = get_random_string()
        encrypted_password = pbkdf2(cleartext_password, salt)
        if groups is None:
            groups = []
        newgroups = set()
        for group in groups:
            group = self._convert(group)
            newgroups.add(group)
        userid = self._convert(userid)
        login = self._convert(login)
        info = {
            'login': login,
            'id': userid,
            'salt': salt,
            'password': encrypted_password,
            'groups': newgroups
        }
        if userid in self.data:
            raise ValueError('User ID "%s" already exists' % userid)
        if login in self.logins:
            raise ValueError('Login "%s" already exists' % login)
        self.logins[login] = userid
        self.data[userid] = info

        for group in newgroups:
            userids = self.groups.get(group, set())
            self.groups[group] = userids  # trigger persistence
            userids.add(userid)

    def remove(self, userid):
        self._upgrade()
        userid = self._convert(userid)
        info = self.data[userid]
        login = info['login']
        del self.logins[login]
        for group in info['groups']:
            userids = self.groups.get(group, [])
            if userid in userids:
                self.groups[group] = userids  # trigger persistence
                userids.remove(userid)
        del self.data[userid]

    def change_password(self, userid, password):
        self._upgrade()
        userid = self._convert(userid)
        info = self.data[userid]
        if 'salt' not in info:
            info['salt'] = get_random_string()
        self.data[userid] = info  # trigger persistence
        info['password'] = pbkdf2(password, info['salt'])

    def change_login(self, userid, login):
        self._upgrade()
        userid = self._convert(userid)
        login = self._convert(login)
        info = self.data[userid]
        old_login = info['login']
        if old_login == login:
            # no change
            return
        if login in self.logins:
            raise ValueError('Login "%s" already exists' % login)
        self.data[userid] = info  # trigger persistence
        info['login'] = login
        self.logins[login] = userid
        del self.logins[old_login]

    def add_user_to_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        info = self.data[userid]
        self.data[userid] = info  # trigger persistence
        info['groups'].add(group)
        userids = self.groups.setdefault(group, set())
        self.groups[group] = userids  # trigger persistence
        userids.add(userid)

    add_group = add_user_to_group

    def remove_user_from_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        info = self.data[userid]
        groups = info['groups']
        if group in groups:
            self.data[userid] = info  # trigger persistence
            groups.remove(group)
        userids = self.groups.get(group)
        if userids is not None:
            if userid in userids:
                self.groups[group] = userids  # trigger persistence
                userids.remove(userid)

    remove_group = remove_user_from_group

    def member_of_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        userids = self.groups.get(group, set())
        return userid in userids

    in_group = member_of_group

    def delete_group(self, group):
        self._upgrade()
        group = self._convert(group)
        userids = self.groups.get(group)
        if userids is not None:
            del self.groups[group]
            for userid in userids:
                info = self.data.get(userid)
                if info is not None:
                    infogroups = info['groups']
                    if group in infogroups:
                        self.data[userid] = info  # trigger persistence
                        infogroups.remove(group)

    def users_in_group(self, group):
        self._upgrade()
        return self.groups.get(group, set())

    def check_password(self, password, userid=None, login=None):
        if userid is None and login is None:
            raise ValueError("Must provide userid or login")
        if userid is not None:
            user = self.get(userid=userid)
        else:
            login = self._convert(login)
            userid = self.logins.get(login)
            user = self.get(login=login)

        if user['password'].startswith('SHA1:'):
            # old style password, need to upgrade but will check it first
            enc_password = get_sha_password(password)
            if strings_same(enc_password, user['password']):
                # upgrade this password...
                salt = get_random_string()
                user.update({'password': pbkdf2(password, salt), 'salt': salt})
                self.data[userid] = user  # trigger persistence
                return True
            else:
                return False
        else:
            # should be 'pbkdf2' encrypted now
            return strings_same(pbkdf2(password, user['salt']),
                                user['password'])
示例#18
0
文件: users.py 项目: lslaz1/karl
class Users(Persistent):
    implements(IUsers)
    data = None

    def __init__(self):
        self.data = OOBTree()
        self.byid = self.data  # b/c
        self.logins = OOBTree()
        self.groups = OOBTree()

    def _convert(self, s):
        if isinstance(s, basestring):
            if not isinstance(s, unicode):
                s = unicode(s, 'utf-8')
        return s

    def _upgrade(self):
        # older revisions of this class used 2 btrees: "bylogin" and
        # "byid", instead of a "data" btree, a "groups" btree, and a
        # "logins" btree; this method upgrades the persistent
        # state of old instances
        if self.data is None:
            self.data = self.byid
            self.logins = OOBTree()
            self.groups = OOBTree()
            for login, info in self.bylogin.items():
                login = self._convert(login)
                userid = self._convert(info['id'])
                self.logins[login] = userid
                groups = info['groups']
                for group in groups:
                    group = self._convert(group)
                    groupset = self.groups.setdefault(group, set())
                    groupset.add(userid)
                    self.groups[group] = groupset
            del self.bylogin

    def get_by_login(self, login):
        # b/c
        return self.get(login=login)

    def get_by_id(self, userid):
        # b/c
        return self.get(userid=userid)

    def get(self, userid=None, login=None):
        self._upgrade()
        if userid is not None and login is not None:
            raise ValueError('Only one of userid or login may be supplied')

        if userid is not None:
            userid = self._convert(userid)
            return self.data.get(userid)

        if login is not None:
            login = self._convert(login)
            userid = self.logins.get(login)
            if userid is None:
                return None
            return self.data.get(userid)

        raise ValueError('Either userid or login must be supplied')

    def add(self, userid, login, cleartext_password, groups=None):
        self._upgrade()
        salt = get_random_string()
        encrypted_password = pbkdf2(cleartext_password, salt)
        if groups is None:
            groups = []
        newgroups = set()
        for group in groups:
            group = self._convert(group)
            newgroups.add(group)
        userid = self._convert(userid)
        login = self._convert(login)
        info = {
            'login': login,
            'id': userid,
            'salt': salt,
            'password': encrypted_password,
            'groups': newgroups}
        if userid in self.data:
            raise ValueError('User ID "%s" already exists' % userid)
        if login in self.logins:
            raise ValueError('Login "%s" already exists' % login)
        self.logins[login] = userid
        self.data[userid] = info

        for group in newgroups:
            userids = self.groups.get(group, set())
            self.groups[group] = userids  # trigger persistence
            userids.add(userid)

    def remove(self, userid):
        self._upgrade()
        userid = self._convert(userid)
        info = self.data[userid]
        login = info['login']
        del self.logins[login]
        for group in info['groups']:
            userids = self.groups.get(group, [])
            if userid in userids:
                self.groups[group] = userids  # trigger persistence
                userids.remove(userid)
        del self.data[userid]

    def change_password(self, userid, password):
        self._upgrade()
        userid = self._convert(userid)
        info = self.data[userid]
        if 'salt' not in info:
            info['salt'] = get_random_string()
        self.data[userid] = info  # trigger persistence
        info['password'] = pbkdf2(password, info['salt'])

    def change_login(self, userid, login):
        self._upgrade()
        userid = self._convert(userid)
        login = self._convert(login)
        info = self.data[userid]
        old_login = info['login']
        if old_login == login:
            # no change
            return
        if login in self.logins:
            raise ValueError('Login "%s" already exists' % login)
        self.data[userid] = info  # trigger persistence
        info['login'] = login
        self.logins[login] = userid
        del self.logins[old_login]

    def add_user_to_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        info = self.data[userid]
        self.data[userid] = info  # trigger persistence
        info['groups'].add(group)
        userids = self.groups.setdefault(group, set())
        self.groups[group] = userids  # trigger persistence
        userids.add(userid)

    add_group = add_user_to_group

    def remove_user_from_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        info = self.data[userid]
        groups = info['groups']
        if group in groups:
            self.data[userid] = info  # trigger persistence
            groups.remove(group)
        userids = self.groups.get(group)
        if userids is not None:
            if userid in userids:
                self.groups[group] = userids  # trigger persistence
                userids.remove(userid)

    remove_group = remove_user_from_group

    def member_of_group(self, userid, group):
        self._upgrade()
        userid = self._convert(userid)
        group = self._convert(group)
        userids = self.groups.get(group, set())
        return userid in userids

    in_group = member_of_group

    def delete_group(self, group):
        self._upgrade()
        group = self._convert(group)
        userids = self.groups.get(group)
        if userids is not None:
            del self.groups[group]
            for userid in userids:
                info = self.data.get(userid)
                if info is not None:
                    infogroups = info['groups']
                    if group in infogroups:
                        self.data[userid] = info  # trigger persistence
                        infogroups.remove(group)

    def users_in_group(self, group):
        self._upgrade()
        return self.groups.get(group, set())

    def check_password(self, password, userid=None, login=None):
        if userid is None and login is None:
            raise ValueError("Must provide userid or login")
        if userid is not None:
            user = self.get(userid=userid)
        else:
            login = self._convert(login)
            userid = self.logins.get(login)
            user = self.get(login=login)

        if user['password'].startswith('SHA1:'):
            # old style password, need to upgrade but will check it first
            enc_password = get_sha_password(password)
            if strings_same(enc_password, user['password']):
                # upgrade this password...
                salt = get_random_string()
                user.update({
                    'password': pbkdf2(password, salt),
                    'salt': salt
                })
                self.data[userid] = user  # trigger persistence
                return True
            else:
                return False
        else:
            # should be 'pbkdf2' encrypted now
            return strings_same(
                pbkdf2(password, user['salt']),
                user['password'])