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
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
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)
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
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)
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
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
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 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)
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()
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)
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
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'])
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'])