示例#1
0
文件: blog.py 项目: mrmaple/open_qon
 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()
示例#2
0
文件: blog.py 项目: mrmaple/open_qon
 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()
示例#3
0
文件: poll.py 项目: mrmaple/open_qon
 def __init__(self, creator, title, description, end_date, choices):
     """Create a poll. Choices is a list of strings."""
     
     Watchable.__init__(self)
     
     # PollContainer (set by container when added)
     self.container = None
     
     # User who created poll
     self.creator = creator
     
     # Date created/modified
     self.date = datetime.utcnow()
     
     # End date
     self.end_date = end_date
     
     # Title/Description
     self.title = title
     self.__description = CompressedText(description)
     self.__cached_html = PersistentCache(self._update_html_cache)
     
     # Other settings and vote data
     self.__data = PollData(choices)
     
     # cache of PollData items
     self.__num_votes_cast = None
     
     # poll index within container
     self.__item_index = None
示例#4
0
文件: poll.py 项目: mrmaple/open_qon
class Poll(QonPersistent, Watchable):
    """A single Poll."""

    persistenceVersion = 1
    
    def __init__(self, creator, title, description, end_date, choices):
        """Create a poll. Choices is a list of strings."""
        
        Watchable.__init__(self)
        
        # PollContainer (set by container when added)
        self.container = None
        
        # User who created poll
        self.creator = creator
        
        # Date created/modified
        self.date = datetime.utcnow()
        
        # End date
        self.end_date = end_date
        
        # Title/Description
        self.title = title
        self.__description = CompressedText(description)
        self.__cached_html = PersistentCache(self._update_html_cache)
        
        # Other settings and vote data
        self.__data = PollData(choices)
        
        # cache of PollData items
        self.__num_votes_cast = None
        
        # poll index within container
        self.__item_index = None

    def upgradeToVersion1(self):
        self.title = iso_8859_to_utf_8(self.title)
        
    def get_data(self):
        return self.__data
    
    def get_description(self):
        return self.__description.get_raw()
    
    def set_description(self, raw):
        self.__description.set_raw(raw)
        self.invalidate_html_cache()
        
    def has_voted(self, user):
        """True if user has already voted."""
        return self.__data.has_voted(user)
        
    def user_vote(self, user):
        try:
            return self.__data.user_vote(user)
        except KeyError:
            return []

    def user_vote_choices(self, user):
        """Return list of strings representing user's choices."""
        choices = self.user_vote(user)
        
        pd = self.get_data()
        votes = []
        for v in choices:
            votes.append(pd.choices[v])
        
        return votes
    
    def user_votes_choices(self):
        """Returns dict of {user_id: [string1, string2, ...]} of user votes."""
        vote_choice_data = self.__data.user_votes()
        
        vote_data = {}
        for user_id, choices in vote_choice_data.iteritems():
            vote_data[user_id] = [self.__data.choices[i] for i in choices]
        return vote_data

    def is_active(self, as_of=None):
        """Return True if Poll is currently active."""
        if not as_of:
            as_of = datetime.utcnow()
            
        is_active = as_of < self.end_date
        
        # check if we need to update our watchable_changed status
        if not is_active:
            if self.watchable_last_change() < self.end_date:
                self.watchable_changed()
        
        return is_active
    
    def cancel_poll(self, note=None):
        """Immediately ends poll, inserting note if any at beginning of description."""
        self.end_date = datetime.utcnow()
        if note:
            self.set_description(note + '\n\n\n' + self.get_description())
            
        # set changed when poll is canceled
        self.watchable_changed(self.end_date)
        
    def num_votes_cast(self):
        if not self.__num_votes_cast:
            self.__num_votes_cast = self.__data.num_votes_cast()
        return self.__num_votes_cast
        
    def valid_vote(self, user, choices):
        """Check various permissions to vote. If choices is not None, also checks validity
        of choices. Use can_vote to pre-check user's access before validity of choices.
        
        Returns:
            1: if user is allowed to vote and choices includes required votes.
            0: if choices are not valid
            -1: if user does not have access to vote
            -2: user has already voted
            -3: user has insufficient feedback score to vote
            -4: user has insufficnent bank to vote
            -5: user joined ned.com after poll started
        """
        
        # general vote access
        if self.__data.vote_access == 'owner':
            if not self.container.ihb.is_owner(user):
                return -1
        elif self.__data.vote_access == 'member':
            if not self.container.ihb.is_member(user) and not self.container.ihb.is_owner(user):
                return -1
        
        # min karma
        if not self.enough_karma_to_vote(user):
            return -3
        
        # karma bank cost
        if user.get_karma_bank_balance() < self.__data.karma_cost:
            return -4
        
        # valid choices
        if choices and not self.__data.choices_valid(choices):
            return 0
        
        # check if trying to vote for no choices
        if choices is not None and not choices:
            return 0
        
        if not self.__data.voter_can_revise:
            if self.__data.has_voted(user):
                return -2
        
        if not self.old_enough_to_vote(user):
            return -5
        
        return 1
        
    def can_pay_for_vote(self, user):
        if not user:
            return False
        return user.can_give_karma(self.get_data().karma_cost)

    def enough_karma_to_vote(self, user):
        return user.get_karma_score() >= self.get_data().min_karma

    def old_enough_to_vote(self, user):
        return user.get_user_data().member_since() <= self.date

    def can_vote(self, user):
        return self.valid_vote(user, choices=None) == 1
    
    def can_see_intermediate_results(self, user):
        pd = self.get_data()
        
        vote_required = pd.vote_required_to_view
        has_voted = self.has_voted(user)
        
        if vote_required and not has_voted:
            return False
        
        if pd.intermediate_access == 'none':
            return False
        if pd.intermediate_access == 'all':
            return True
        if pd.intermediate_access == 'member':
            return self.container.ihb.is_member(user) or self.container.ihb.is_owner(user)
        if pd.intermediate_access == 'owner':
            return self.container.ihb.is_owner(user)
        return False
        
    def can_see_results(self, user):
        pd = self.get_data()
                
        if pd.results_access == 'all':
            return True
        if pd.results_access == 'member':
            return self.container.ihb.is_member(user) or self.container.ihb.is_owner(user)
        if pd.results_access == 'owner':
            return self.container.ihb.is_owner(user)
        return False
        
    def record_vote(self, user, choices):
        """Record a vote. Raises ValueError if valid_vote() check fails.
        
        Fails silently if poll is no longer open or if user can't pay.
        """
        
        if not self.is_active():
            return
        
        if self.valid_vote(user, choices) != 1:
            raise ValueError
            
        # see if new vote is identical to previous vote
        if self.has_voted(user):
            if choices == self.user_vote(user):
                return
            
        if not _ROLLOUT_TEST:
            # try to pay for item
            try:
                user.pay_karma(self.get_data().karma_cost)
            except NoKarmaToGive:
                return
        
        self.__data.record_vote(user, choices)
        self.__num_votes_cast = None
        
        # karma credit
        user.karma_activity_credit()
    
    def choice_list_to_choices(self, choice_list):
        """Convert a list of strings to their corresponding list of indices.
        
        Raises ValueError if there is an invalid item in choice_list.
        """
        pd = self.get_data()
        choices = []
        for item in choice_list:
            choices.append(pd.choices.index(item))
        return choices
        
    def _set_item_index(self, index):
        """Used by PollContainer to set this item's index when added to container."""
        self.__item_index = index
    
    def get_item_index(self):
        return self.__item_index

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

    def invalidate_html_cache(self):
        self.__cached_html.flush()
        
    def get_cached_html(self):
        return self.__cached_html.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_description(),
            wiki=self.container.ihb.get_wiki(),
            container=self)))

    def disable_cache(self):
        self.__cached_html.disable_cache()

    def cache_disabled(self):
        return self.__cached_html.cache_disabled()        
            
    def watchable_name(self):
        return self.title

    def watchable_changed(self, now=None):
        # tells container he has changed, too
        Watchable.watchable_changed(self, now)
        self.container.watchable_changed(now)

    def can_read(self, user):
        return self.container.ihb.can_read(user)        
示例#5
0
文件: blog.py 项目: mrmaple/open_qon
 def set_main(self, raw):
     if not self.__main:
         self.__main = CompressedText()
     self.__main.set_raw(raw)
示例#6
0
文件: blog.py 项目: mrmaple/open_qon
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