def __init__(self, wiki, name=''): Watchable.__init__(self) qon.karma.HasKarma.__init__(self) self.wiki = wiki self.outbound_references = None self.inbound_references = None self.name = clean_page_name(name) self.versions = PersistentList() self.blog = Blog(self) self.locked_by_user = None self.__cached_html = PersistentCache(self._update_html_cache) self.__cached_html2 = PersistentCache(self._update_html2_cache) self.new_revision(force_new=1)
class WikiPage(QonPersistent, Watchable, qon.karma.HasKarma, IHasBlog): persistenceVersion = 4 def __init__(self, wiki, name=''): Watchable.__init__(self) qon.karma.HasKarma.__init__(self) self.wiki = wiki self.outbound_references = None self.inbound_references = None self.name = clean_page_name(name) self.versions = PersistentList() self.blog = Blog(self) self.locked_by_user = None self.__cached_html = PersistentCache(self._update_html_cache) self.__cached_html2 = PersistentCache(self._update_html2_cache) self.new_revision(force_new=1) def upgradeToVersion4(self): self.inbound_references = None self.version_upgrade_done() def upgradeToVersion3(self): self.__cached_html2 = PersistentCache(self._update_html2_cache) self.version_upgrade_done() def upgradeToVersion2(self): self.__cached_html = PersistentCache(self._update_html_cache) self.version_upgrade_done() def upgradeToVersion1(self): self.blog.ihb = self self.version_upgrade_done() def __repr__(self): return '<%s object at 0x%x: %s>' % (self.__module__ + '.' + self.__class__.__name__, id(self), self.name or "*no name*") def new_revision(self, set_date=True, author=None, title='', raw='', force_new=0): """Create a new revision for this page. Check to make sure that the new text is actually different from the latest revision. If it's not, then don't bother creating a new revision.""" if force_new or (self.versions[-1].get_raw() != raw): w = WikiVersion(page=self, author=author, title=title, raw=raw) if set_date: w.set_date(datetime.utcnow()) self.versions.append(w) self.watchable_changed(w.date) if author: author.karma_activity_credit() # before invalidating referring pages, we want to # update the html cache, which has the side effect # of updating the outbound references. self.invalidate_html_cache() unused_html = self.get_cached_html() # may seem useless for new pages, but we could be creating # a new page that was referred to from another page somewhere self._invalidate_referring_pages() self._p_changed = 1 def _invalidate_referring_pages(self, all_groups=0): """Invalidate HTML cache of pages which refer to this one.""" # we changed default behavior to not scan all groups when invalidating. # this means that cross-group links for new pages after this change # will not be accurate, until the page(s) linking to the new page # is itself modified. refs = self.wiki.references_to(self, all_groups=all_groups) for p in refs: p.invalidate_html_cache() def latest_edit_by(self, user): """Return latest edit by user, or None.""" rvers = self.versions[:] rvers.reverse() for version in rvers: if version.author is user: return version return None def get_comments(self): """Return list of comments (BlogItems).""" blog_item = self.blog.get_item(0) if blog_item: return blog_item.get_comments() else: return [] def get_revision(self, rev_id): """Return revision index rev_id or None.""" rev_id = max(0, rev_id) try: rev = self.versions[rev_id] except IndexError: rev = None return rev def revision_index(self, version): """Return revision index of version, or raise ValueError.""" return self.versions.index(version) def merge_revisions(self, base, old, new): """Merge the newest revision with older revision, off of base. Returns (merged text, exit_code) or None. Base may be -1 to signify empty text. Exit code is 0 for no conflicts, or 1 if conflicts exist. """ if len(self.versions) < 2: return None if base == -1: base_text = '' else: base_text = self.versions[base].get_raw() old_text = self.versions[old].get_raw() new_text = self.versions[new].get_raw() merger = Merger(base_text, old_text, new_text) merged = merger.merge('Revision %d' % base, 'Revision %d' % old, 'Revision %d' % new, ) if not merged: return None exit_code = 0 if merger.has_conflicts(): exit_code = 1 return (merged, exit_code) def watchable_name(self): #return self.wiki.group.name + ' ' + self.versions[-1].title return self.versions[-1].title or self.name def watchable_changed(self, now=None): # wiki changed, too Watchable.watchable_changed(self, now) self.wiki.watchable_changed(now) def watchable_modified_date(self): return self.watchable_last_change() def last_modified(self): sys.stderr.write('WARNING: using deprecated qon.wiki.WikiPage.last_modified.') return self.watchable_last_change() def who_has_lock(self): return self.locked_by_user def can_edit(self, user): """ A page is editable if either it's not locked by anybody, or if the requesting user is the one who holds the lock, or if the user is allowed to edit within the group""" # user must be logged in to edit if not user: return False # check lock if (self.locked_by_user) and (self.locked_by_user is not user) and (not self.can_manage(user)): return False if self.wiki.group.can_edit(user): return True return False 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 return True def can_lock(self, user): """ For now, let only a group owner lock/unlock a page. In the future, we may want to consider allowing the original page author to lock/unlock as well.""" return self.wiki.group.is_owner(user) def lock(self, user): if self.can_lock(user): self.locked_by_user = user def unlock(self, user): if self.can_lock(user): self.locked_by_user = None def can_get_karma_from(self, other): return other is not None # HTML cache methods def add_html_dependency(self, target): """Adds target as something self depends on for its HTML cache.""" self.__cached_html.add_dependency(target) self.__cached_html2.add_dependency(target) def invalidate_html_cache(self): self.__cached_html.flush() self.__cached_html2.flush() def get_cached_html(self): return self.__cached_html.get() def get_cached_html2(self): return self.__cached_html2.get() def _update_html_cache(self): v = self.versions[-1] html = v.raw_to_html(v.get_raw()) # take this opportunity to update the page's outbound references if hasattr(v, '_v_references'): self.set_outbound_references(v._v_references) del v._v_references return html def _update_html2_cache(self): v = self.versions[-1] html = v.raw_to_html(v.get_raw(), suppress_tooltip=1) return html def disable_cache(self): self.__cached_html.disable_cache() self.__cached_html2.disable_cache() def cache_disabled(self): return self.__cached_html.cache_disabled() or self.__cached_html2.cache_disabled() def get_ref(self): """Return a reference (group, page_name) to this page, for use in outbound/inbound references.""" return (self.wiki.group, self.name) def set_outbound_references(self, new_out_refs): """Record new outbound references.""" # filter non-existent cross-group page refs out of new_out_refs # this interacts with the change that no longer scans all groups # for references to new pages. if a cross-group link existed to a new # page, this method (pre-filtering) would have neglected to add the inbound # link from the cross-group reference, even if both pages had been edited. l = [] for r in new_out_refs: group_name, page_name = r if not group_name: l.append(r) else: page = _ref_to_page(r, self.wiki.group) if page: l.append(r) new_out_refs = l # get old outbound refs old_out_refs = self.outbound_references or [] # get two lists: items that used to be outbound references but # are no longer (old_not_new), and new outbound references that # weren't there before (new_not_old) old_not_new, new_not_old = xor_lists(old_out_refs, new_out_refs) # pre-fill reference to me me_ref = self.get_ref() # invalidate inbound references of pages that we no longer refer to for ref in old_not_new: page = _ref_to_page(ref, self.wiki.group) if page: # added by Alex page.remove_inbound_reference(me_ref) # add inbound references for pages we've added outbound links to for ref in new_not_old: page = _ref_to_page(ref, self.wiki.group) if page: # could be ref to new page page.add_inbound_reference(me_ref) # record new outbound references self.outbound_references = PersistentList() self.outbound_references.extend(new_out_refs) def remove_inbound_reference(self, ref): if self.inbound_references is not None: if ref in self.inbound_references: self.inbound_references.remove(ref) def add_inbound_reference(self, ref): if self.inbound_references is not None: if ref not in self.inbound_references: self.inbound_references.append(ref) # IHasBlog methods not implemented by other base classes def can_manage(self, user): """Who can manage this blog? Group owners.""" return self.wiki.group.is_owner(user) def can_read(self, user): return self.wiki.can_read(user) def can_delete_item(self, item): """Can't delete item 0, which holds page comments.""" if self.blog.get_item(0) is item: return False return True def can_create_item(self): """Users aren't allowed to create new topics in wiki pages.""" return False def is_accepted(self): return self.wiki.group.is_accepted() def get_owners(self): return self.wiki.group.get_owners() def is_owner(self, user): return self.wiki.group.is_owner(user) def get_title(self): # this is here and in BlogItem return self.versions[-1].title or self.name def get_blog(self): return self.blog def get_wiki(self): return self.wiki def get_name(self): return self.name def get_all_owners(self): return self.get_owners() def get_all_blogs(self): return [self.blog] def get_member_list(self): return self.wiki.group.get_member_list()