Beispiel #1
0
class DeferralList(QonPersistent):
    """Provides simple timed deferral mechanism.
    
    Maintains a list of keyed times. Client calls defer()
    to add a key and a delay. Subsequent calls to defer()
    with the same key will return True if delay has elapsed,
    or false if not.
    """
    
    persistenceVersion = 1
    
    def __init__(self):
        self.defers = ConflictAvoidingOOBTree()
        
    def upgradeToVersion1(self):
        # upgrade from PersistentMapping

        bt = ConflictAvoidingOOBTree()

        for k,v in self.defers.iteritems():
            bt[k] = v

        del self.defers
        self.defers = bt
        self.version_upgrade_done()
        
    def defer(self, key, delay):
        """Schedule a deferred event labeled by key.
        
        If key already exists, returns True if enough
        time has elapsed since key was originally added.
        Otherwise, add key and return False.
        """
        now = datetime.utcnow()
        
        if self.defers.has_key(key):
            if self.defers[key] <= now:
                del self.defers[key]
                return True
            else:
                return False
        else:
            self.defers[key] = now + delay
            return False
    
    def cancel(self, key):
        """Unschedule a deferred task."""
        if self.defers.has_key(key):
            del self.defers[key]
Beispiel #2
0
 def __init__(self, blog, author, title, summary, main='', dont_watch=0):
     qon.karma.HasKarma.__init__(self)
     Watchable.__init__(self)
     HasComments.__init__(self)
     self.blog = blog
     self.__deleted = 0
     self.author = author
     self.title = title
     self.__summary = CompressedText(summary)
     if main:
         self.__main = CompressedText(main)
     else:
         self.__main = None
     self.__cached_html_summary = PersistentCache(self._update_html_cache)
     # history is a string, showing diffs as items are edited
     self.history = None
     self.date = datetime.utcnow()
     self.modified = None
     self.parent_blogitem = None     # for comment, will point to parent blogitem upon add_comment(); otherwise None
     
     if dont_watch:
         """Comments aren't watchable."""
         self.not_watchable = 1
     else:
         # for watchable items only (not comments)
         self.__user_access = ConflictAvoidingOOBTree()
Beispiel #3
0
    def upgradeToVersion3(self):
        if not self.not_watchable:
            self.__user_access = ConflictAvoidingOOBTree()

        # get rid of old Readers attribute
        if hasattr(self, '_BlogItem__readers'):
            del self.__readers
        
        self.version_upgrade_done()        
Beispiel #4
0
class IPAddresses(QonPersistent):
    """Container to track IP addresses."""

    _resolution = timedelta(minutes=15)
    
    def __init__(self):
        self.__items = ConflictAvoidingOOBTree()
    
    def ip_hit(self, ip, dt=None):
        """Record a hit from ip at dt (or now). Only records if more than
        _resolution time has elapsed.
        """
        from qon.util import pack_ip

        try:
            packed_ip = pack_ip(ip)
        except:
            # a problem in format of ip
            return

        dt = dt or datetime.utcnow()

        last_hit = self.__items.get(packed_ip, None)
        if last_hit:
            if dt - last_hit < self._resolution:
                return

        self.__items[packed_ip] = dt
    
    def get_ips(self):
        """Return dict of [IP.Address] -> datetime, ..."""
        from qon.util import unpack_ip
        ips = {}
        for k, v in self.__items.iteritems():
            ips[unpack_ip(k)] = v
        return ips
Beispiel #5
0
 def __init__(self):
     self.defers = ConflictAvoidingOOBTree()
Beispiel #6
0
 def __init__(self):
     self.__items = ConflictAvoidingOOBTree()
     self.__footprints = ConflictAvoidingPersistentList()
Beispiel #7
0
class WatchList(QonPersistent):
    """Keep track of a collection of Watchable items by their oids.
    
    Stores last-seen time for each object watched.
    """
    
    persistenceVersion = 3
    
    _max_footprints = 10
    
    def __init__(self):
        self.__items = ConflictAvoidingOOBTree()
        self.__footprints = ConflictAvoidingPersistentList()
        
    def upgradeToVersion3(self):
        """Make footprints persistent. No need to del _v_footprints since it's volatile anyway."""
        self.__footprints = ConflictAvoidingPersistentList()
        self.version_upgrade_done()
        
    def upgradeToVersion2(self):
        newbt = ConflictAvoidingOOBTree(self.__items)
        del self.__items
        self.__items = newbt
        self.version_upgrade_done()
        
    def upgradeToVersion1(self):
        if hasattr(self, '_WatchList__footprints'):
            del self.__footprints
        
    def watch_item(self, item):
        """Add item to watch list. Records current time as last-seen."""
        typecheck(item, Watchable)
        typecheck(item, Persistent)
        if not item.watchable():
            return
            
        if not self.__items.has_key(item._p_oid):
            self.__items[item._p_oid] = datetime.utcnow()
    
    def stop_watching_item(self, item):
        """Remove item from watch list."""
        typecheck(item, Watchable)
        typecheck(item, Persistent)
        if not item.watchable():
            return
        if self.__items.has_key(item._p_oid):
            del self.__items[item._p_oid]
    
    def watchable_seen(self, obj):
        """Called when a watchable item is seen, even if
        it's not in my watchable list."""
        typecheck(obj, Watchable)
        typecheck(obj, Persistent)
        if not obj.watchable():
            return
        if self.__items.has_key(obj._p_oid):
            self.__items[obj._p_oid] = datetime.utcnow()
        
        self.add_footprint(obj)
    
    def changed_items(self, since):
        """Return list of items changed since datetime since."""
        watched = self.watched_items()
        changed = [item for item in watched if item.changed_since(since)]
        return changed
        
    def changed_unseen_items(self):
        """Return list of items changed and unseen since datetime since."""
        changed = self.watched_items()
        return [item for item in changed \
            if self.last_seen(item) < item.watchable_last_change()]
    
    def watched_items(self):
        """Return list of all items being watched."""
        return self._get_items(self.__items.keys())
        
    def watched_items_oids(self):
        """Return list of all item OIDs being watched."""
        return self.__items.keys()
        
    def _get_items(self, oid_list):
        """Return a list of valid watchable items from oid_list"""
        items = []
        for k in oid_list:
            try:
                obj = get_oid(k)
                obj_name = obj.watchable_name()
            except:
                self._lost_oid(k)
            else:
                items.append(obj)
        return items
        
    def _lost_oid(self, oid):
        """We can't find this oid, remove from list."""
        if self.__items.has_key(oid):
            del self.__items[oid]
        while oid in self.__footprints:
            self.__footprints.remove(oid)
        
    def footprints(self):
        """Return list of footprinted items, most recent first."""
        items = self._get_items(self.__footprints)
        items.reverse()
        return items
        
    def add_footprint(self, obj):
        """Add obj to fooprint list."""
        self.__footprints.append(obj._p_oid)
        if len(self.__footprints) > self._max_footprints:
            self.__footprints = ConflictAvoidingPersistentList(self.__footprints[-self._max_footprints:])
        remove_left_duplicates(self.__footprints)
        
    def last_seen(self, item):
        """Return datetime this item was last seen."""
        typecheck(item, Watchable)
        typecheck(item, Persistent)
        return self.__items.get(item._p_oid, never)
        
    def is_watching(self, item):
        """Returns true if item is in watch list."""
        typecheck(item, Watchable)
        typecheck(item, Persistent)
        return item._p_oid in self.__items
Beispiel #8
0
 def __init__(self):
     self.__items = ConflictAvoidingOOBTree()
Beispiel #9
0
class BlogItem(QonPersistent, qon.karma.HasKarma, Watchable, HasComments):

    persistenceVersion = 6

    def __init__(self, blog, author, title, summary, main='', dont_watch=0):
        qon.karma.HasKarma.__init__(self)
        Watchable.__init__(self)
        HasComments.__init__(self)
        self.blog = blog
        self.__deleted = 0
        self.author = author
        self.title = title
        self.__summary = CompressedText(summary)
        if main:
            self.__main = CompressedText(main)
        else:
            self.__main = None
        self.__cached_html_summary = PersistentCache(self._update_html_cache)
        # history is a string, showing diffs as items are edited
        self.history = None
        self.date = datetime.utcnow()
        self.modified = None
        self.parent_blogitem = None     # for comment, will point to parent blogitem upon add_comment(); otherwise None
        
        if dont_watch:
            """Comments aren't watchable."""
            self.not_watchable = 1
        else:
            # for watchable items only (not comments)
            self.__user_access = ConflictAvoidingOOBTree()

    def upgradeToVersion6(self):
        self.history = None

    def upgradeToVersion5(self):
        self.title = iso_8859_to_utf_8(self.title)

    def upgradeToVersion4(self):
        self.__deleted = self.deleted
        del self.deleted

        # upgrade HasComments
        HasComments._upgradeToVersion1(self)

        # elim __main's CompressedText
        if not self.get_main():
            self.__main = None

        self.version_upgrade_done()        
        
    def upgradeToVersion3(self):
        if not self.not_watchable:
            self.__user_access = ConflictAvoidingOOBTree()

        # get rid of old Readers attribute
        if hasattr(self, '_BlogItem__readers'):
            del self.__readers
        
        self.version_upgrade_done()        
        
    def upgradeToVersion2(self):
        # do self.parent_blogitem 2005-03-17
        self.parent_blogitem = None        
        comments = HasComments.get_all_comments(self)
        for item in comments:
            item.parent_blogitem = self # point comments to point to itself
             
        self.version_upgrade_done()        
                
    def upgradeToVersion1(self):
    
        # compress text
        self.__summary = CompressedText(self.summary)
        self.__main = CompressedText(self.main)
        del self.summary
        del self.main
        
        # create cache
        self.__cached_html_summary = PersistentCache(self._update_html_cache)
        
        self.version_upgrade_done()
            
    def is_deleted(self, use_attr=False):
        if use_attr or not self.parent_blogitem:
            return self.__deleted

        return self.parent_blogitem.get_comment_flags(self)

    def set_deleted(self, val):
        self.__deleted = bool(val)

        # cache/copy deleted attribute into paren't comment_flags
        # for fast lookup to avoid db reading each comment
        if self.parent_blogitem:
            self.parent_blogitem.set_comment_flags(self, bool(val))

        # alex added so that recent items cache for parent blog gets marked as dirty
        self.watchable_changed()        

    def set_deleted_note(self, note):
        self._deleted_note = note

    def get_deleted_note(self):
        return getattr(self, '_deleted_note', None)

    def can_read(self, user):
        return self.blog.ihb.can_read(user)
    
    def can_edit(self, user):
        return (self.author is user) or self.can_manage(user)
    
    def can_delete(self, user):
        """Return True if user can delete this item."""
        
        # managers can always delete
        if self.can_manage(user):
            return True
            
        # authors can only delete if there are no undeleted comments
        if self.num_comments() == 0:
            return self.author is user
        
        return False
    
    def can_manage(self, user):
        """Return True if user can manage this item (usually a group owner)."""
        return self.blog.ihb and self.blog.ihb.can_manage(user)
        
    def can_show(self):
        """Return False if this item should be suppressed due to feedback score."""
        if self.get_karma_score() < qon.karma.min_karma_to_show:
            return False
        
        if self.author.get_karma_score() < qon.karma.min_author_karma:
            return False
        
        return True

    def why_cant_show(self):
        """Only really useful when can_show()==False.  Returns the reason for an item
        not being shown.  Return value is ('item' | 'user', fbscore)"""
        if self.get_karma_score() < qon.karma.min_karma_to_show:
            return ('item', self.get_karma_score())

        if self.author.get_karma_score() < qon.karma.min_author_karma:
            return ('user', self.author.get_karma_score())

        return ()
                            
    def last_modified(self, consider_comments=True):
        if consider_comments:
            # this Watchable changes whenever a comment is added
            dt = self.watchable_last_change()
            if dt is never:
                dt = self.modified or self.date
            return dt
        else:
            return self.modified or self.date
        
    def new_comment(self, author, title, summary, main=''):
        """Create a new comment item and return it."""
        comment = BlogItem(blog=self.blog,
            author=author,
            title=title,
            summary=summary,
            main=main,
            dont_watch=1)
        
        # Check to see if this new comment is a duplicate
        #  of the previous comment.  If so, ignore it, since
        #  it's probably unintended, and just return None.
        if HasComments.is_duplicate(self, comment):
            comment = None
        else:            
            self.add_comment(comment)

            # avoid 'bogus new to me'
            comment.read_item(author, datetime.utcnow())

            author.karma_activity_credit()
        return comment
        
    def notify_karma_changed(self):
        """Called by HasKarma.add_karma."""
        
        # also delegate to watchable_changed
        # self.watchable_changed() # removed by alex to keep blogitems from boldfacing when left feedback
        
        if self.blog and hasattr(self.blog, 'notify_karma_changed'):
            self.blog.notify_karma_changed()

    def add_comment(self, comment):
        comment.parent_blogitem = self        
        HasComments.add_comment(self, comment)
        self.watchable_changed(comment.date)
    
    def watchable_name(self):
        return self.title
    
    def watchable_changed(self, now=None):
        # tells blog he has changed, too
        Watchable.watchable_changed(self, now)
        if self.blog:
            self.blog.watchable_changed(now)

    def watchable_modified_date(self):
        return self.last_modified()

    def can_get_karma_from(self, other):
        return other is not self.author
        
    def get_summary(self):
        return self.__summary.get_raw()
        
    def set_summary(self, raw):
        self._log_summary_change()
        self.__summary.set_raw(raw)
        self.invalidate_html_cache()
        
    def get_main(self):
        if not self.__main:
            return ''
        return self.__main.get_raw()
        
    def set_main(self, raw):
        if not self.__main:
            self.__main = CompressedText()
        self.__main.set_raw(raw)
    
    def _log_summary_change(self):
        import qon.log
        if hasattr(self.blog.ihb, 'get_user_id'):
            qon.log.edit_info('SetSummary\t%s\n%s' % (self.blog.ihb.get_user_id(), self.get_summary()))
        else:
            qon.log.edit_info('SetSummary2\t%s\n%s' % (self.blog.ihb.get_name(), self.get_summary()))

    # HTML cache methods
        
    def add_html_dependency(self, target):
        """Adds target as something self depends on for its HTML cache."""
        self.__cached_html_summary.add_dependency(target)

    def invalidate_html_cache(self):
        self.__cached_html_summary.flush()
        
    def get_cached_html(self):
        return self.__cached_html_summary.get().get_raw()

    def _update_html_cache(self):
        from qon.ui.blocks.wiki import rst_to_html
        return CompressedText(str(rst_to_html(self.get_summary(),
            wiki=self.blog.ihb.get_wiki(),
            container=self)))

    def disable_cache(self):
        self.__cached_html_summary.disable_cache()

    def cache_disabled(self):
        return self.__cached_html_summary.cache_disabled()
    
    def read_item(self, user, now=None):
        """Notice that user has accessed this item.
        
        If we are a comment, we pass this on to our parent item, to
        catch up based on comment's submission date.
        """

        if not hasattr(self, "_BlogItem__user_access"):

            # we don't keep track of user access -- we're a comment,
            # so just pass this on to our parent.
            if self.parent_blogitem:
                return self.parent_blogitem.read_item(user, self.date)
            return
        
        if not user:
            return
            
        now = now or datetime.utcnow()
        user_oid = unpack_oid(user._p_oid)
        
        dt, count = self.__user_access.get(user_oid, (never, 0))
        now = max(now, dt)
        
        # increment hit count
        self.__user_access[user_oid] = (now, count + 1)

    def has_read_item(self, user, updated=None):
        if not hasattr(self, "_BlogItem__user_access"):
            return False
        
        if not user:
            return True
        
        dt, count = self._get_user_access(user)
        if count == 0:
            return False
        
        if updated:
            return dt >= updated
        else:
            return True
    
    def last_read(self, user):
        """Return datetime when user last read this item."""
        dt, count = self._get_user_access(user)
        return dt
    
    def _get_user_access(self, user):
        if not hasattr(self, "_BlogItem__user_access"):
            return (never, 0)
            
        user_oid = unpack_oid(user._p_oid)
        return self.__user_access.get(user_oid, (never, 0))
    
    def item_views(self):
        """Returns (number of views, number of readers).""" 
        
        views = 0
        readers = 0
        for user_oid, (dt, count) in self.__user_access.iteritems():
            if count > 0:   # alex added if on 2006-10-09
                readers += 1
                views += count
        
        return (views, readers)
 
    def get_pageview_counts_per_user(self):
        """return a list of (user, counts)"""
        return_list = []
        for user_oid, (dt, count) in self.__user_access.iteritems():
            if count > 0:
                return_list.append((get_oid(pack_oid(user_oid)), count))
        return return_list
            
    def get_title(self):
        # this is here and in WikiPage
        return self.title