def _unpublish(self, save): if self.published_at is None: return cache.del_yaml(self.title) cache.del_rendered_body(self.title) if self.newer_title: cache.del_yaml(self.newer_title) cache.del_rendered_body(self.newer_title) if self.older_title: cache.del_yaml(self.older_title) cache.del_rendered_body(self.older_title) older = WikiPage.get_by_title(self.older_title) newer = WikiPage.get_by_title(self.newer_title) if self.older_title is not None and self.newer_title is not None: newer.older_title = self.older_title older.newer_title = self.newer_title newer.put() older.put() elif self.older_title is not None: older.newer_title = None older.put() elif self.newer_title is not None: newer.older_title = None newer.put() self.published_at = None self.published_to = None self.older_title = None self.newer_title = None if save: self.put()
def _publish(self, title, save): if self.published_at is not None and self.published_to == title: return posts = WikiPage.get_published_posts(title, 20) if len(posts) > 0: latest = posts[0] latest.newer_title = self.title latest.put() self.older_title = latest.title self.published_to = title self.published_at = datetime.now() if save: self.put() cache.del_yaml(self.title) cache.del_rendered_body(self.title) if self.newer_title: cache.del_yaml(self.newer_title) cache.del_rendered_body(self.newer_title) if self.older_title: cache.del_yaml(self.older_title) cache.del_rendered_body(self.older_title)
def update_content(self, new_body, base_revision, comment, user=None, force_update=False): if not force_update and self.body == new_body: return False # delete rendered body cache cache.del_rendered_body(self.title) # update body old_md = self.metadata new_md = WikiPage.parse_metadata(new_body) # validate contents if u'pub' in new_md and u'redirect' in new_md: raise ValueError('You cannot use "pub" and "redirect" metadata at ' 'the same time.') if u'redirect' in new_md and len(WikiPage.remove_metadata(new_body).strip()) != 0: raise ValueError('Page with "redirect" metadata cannot have a body ' 'content.') if u'read' in new_md and new_md['content-type'] != 'text/x-markdown': raise ValueError('You cannot restrict read access of custom content-typed page.') if self.revision < base_revision: raise ValueError('Invalid revision number: %d' % base_revision) # update model fields if self.revision != base_revision: # perform 3-way merge if needed base = WikiPageRevision.query(WikiPageRevision.title == self.title, WikiPageRevision.revision == base_revision).get().body merged = ''.join(Merge3(base, self.body, new_body).merge_lines()) self.body = merged else: self.body = new_body self.modifier = user self.description = self.make_description(200) self.acl_read = new_md.get('read', '') self.acl_write = new_md.get('write', '') self.comment = comment self.revision += 1 if not force_update: self.updated_at = datetime.now() # publish pub_old = u'pub' in old_md pub_new = u'pub' in new_md pub_old_title = None pub_new_title = None if pub_old: pub_old_title = old_md['pub'] if pub_new: pub_new_title = new_md['pub'] if pub_old and pub_new and (pub_old_title != pub_new_title): # if target page is changed self._unpublish(save=False) self._publish(title=pub_new_title, save=False) else: if pub_new: self._publish(title=pub_new_title, save=False) else: self._unpublish(save=False) # update related pages if it's first time if self.revision == 1: for _ in range(5): self.update_related_links() # update itemtype_path self.itemtype_path = schema.get_itemtype_path(self.itemtype) # update hashbangs self.hashbangs = WikiPage.extract_hashbangs(self.rendered_body) # save self.put() # create revision rev_key = self._rev_key() rev = WikiPageRevision(parent=rev_key, title=self.title, body=self.body, created_at=self.updated_at, revision=self.revision, comment=self.comment, modifier=self.modifier, acl_read=self.acl_read, acl_write=self.acl_write) rev.put() # update inlinks and outlinks old_redir = old_md.get('redirect') new_redir = new_md.get('redirect') self.update_links(old_redir, new_redir) # invalidate cache cache.del_yaml(self.title) if self.revision == 1: cache.del_titles() return True
def update_links(self, old_redir=None, new_redir=None): """Updates outlinks of this page and inlinks of target pages""" # 1. process "redirect" metadata if old_redir != new_redir: if old_redir is not None: source = WikiPage.get_by_title(old_redir, follow_redirect=True) else: source = self if new_redir is not None: target = WikiPage.get_by_title(new_redir, follow_redirect=True) else: target = self for rel, titles in source.inlinks.items(): for t in titles: page = WikiPage.get_by_title(t) page.del_outlink(source.title, rel) page.add_outlink(target.title, rel) page.put() cache.del_yaml(page.title) cache.del_rendered_body(page.title) target.add_inlinks(source.inlinks[rel], rel) del source.inlinks[rel] source.put() cache.del_yaml(source.title) cache.del_rendered_body(source.title) target.put() cache.del_yaml(target.title) cache.del_rendered_body(target.title) # 2. update in/out links cur_outlinks = self.outlinks or {} new_outlinks = {} for rel, titles in self._parse_outlinks().items(): new_outlinks[rel] =\ [WikiPage.get_by_title(t, follow_redirect=True).title for t in titles] new_outlinks[rel] = list(set(new_outlinks[rel])) if self.acl_read: # delete all inlinks of target pages if there's read restriction for rel, titles in cur_outlinks.items(): for title in titles: page = WikiPage.get_by_title(title) try: page.del_inlink(title) if len(page.inlinks) == 0 and page.revision == 0: page.put().delete() else: page.put() cache.del_yaml(page.title) cache.del_rendered_body(page.title) except ValueError: pass else: # update all inlinks of target pages added_outlinks = {} for rel, titles in new_outlinks.items(): added_outlinks[rel] = titles if rel in cur_outlinks: added_outlinks[rel] =\ set(added_outlinks[rel]).difference(cur_outlinks[rel]) removed_outlinks = {} for rel, titles in cur_outlinks.items(): removed_outlinks[rel] = titles if rel in new_outlinks: removed_outlinks[rel] =\ set(removed_outlinks[rel]).difference(new_outlinks[rel]) for rel, titles in added_outlinks.items(): for title in titles: page = WikiPage.get_by_title(title) page.add_inlink(self.title, rel) page.put() cache.del_yaml(page.title) cache.del_rendered_body(page.title) for rel, titles in removed_outlinks.items(): for title in titles: page = WikiPage.get_by_title(title, follow_redirect=True) try: page.del_inlink(self.title, rel) if page.inlinks == {} and page.revision == 0: page.put().delete() else: page.put() cache.del_yaml(page.title) cache.del_rendered_body(page.title) except ValueError: pass # update outlinks of this page self.outlinks = new_outlinks for rel in self.outlinks.keys(): self.outlinks[rel].sort() self.put()
def update_content(self, new_body, base_revision, comment="", user=None, force_update=False, dont_create_rev=False): if not force_update and self.body == new_body: return False # get old data amd metadata old_md = self.metadata old_data = self.data # validate contents ## validate schema data new_md = PageOperationMixin.parse_metadata(new_body) try: PageOperationMixin.parse_data(self.title, new_md["schema"], new_body) except Exception: raise ValueError("Invalid schema data") ## validate metadata if u"pub" in new_md and u"redirect" in new_md: raise ValueError('You cannot use "pub" and "redirect" metadata at ' "the same time.") if u"redirect" in new_md and len(PageOperationMixin.remove_metadata(new_body).strip()) != 0: raise ValueError('Page with "redirect" metadata cannot have a body ' "content.") if u"read" in new_md and new_md["content-type"] != "text/x-markdown": raise ValueError("You cannot restrict read access of custom content-typed page.") ## validate revision if self.revision < base_revision: raise ValueError("Invalid revision number: %d" % base_revision) ## validate ToC if not TocGenerator(md.convert(new_body)).validate(): raise ValueError("Duplicate paths not allowed") if self.revision != base_revision: # perform 3-way merge if needed base = ( WikiPageRevision.query(WikiPageRevision.title == self.title, WikiPageRevision.revision == base_revision) .get() .body ) merged = "".join(Merge3(base, self.body, new_body).merge_lines()) conflicted = len(re.findall(PageOperationMixin.re_conflicted, merged)) > 0 if conflicted: raise ConflictError("Conflicted", base, new_body, merged) else: new_body = merged # delete rendered body, metadata, data cache cache.del_rendered_body(self.title) cache.del_hashbangs(self.title) cache.del_metadata(self.title) cache.del_data(self.title) # update model fields self.body = new_body self.modifier = user self.description = self.make_description(200) self.acl_read = new_md.get("read", "") self.acl_write = new_md.get("write", "") self.comment = comment if not dont_create_rev: self.revision += 1 if not force_update: self.updated_at = datetime.now() # publish pub_old = u"pub" in old_md pub_new = u"pub" in new_md pub_old_title = None pub_new_title = None if pub_old: pub_old_title = old_md["pub"] if pub_new: pub_new_title = new_md["pub"] if pub_old and pub_new and (pub_old_title != pub_new_title): # if target page is changed self._unpublish(save=False) self._publish(title=pub_new_title, save=False) else: if pub_new: self._publish(title=pub_new_title, save=False) else: self._unpublish(save=False) # update itemtype_path self.itemtype_path = schema.get_itemtype_path(new_md["schema"]) # save self.put() # create revision if not dont_create_rev: rev_key = self._rev_key() rev = WikiPageRevision( parent=rev_key, title=self.title, body=self.body, created_at=self.updated_at, revision=self.revision, comment=self.comment, modifier=self.modifier, acl_read=self.acl_read, acl_write=self.acl_write, ) rev.put() # deferred update schema data index new_data = self.data deferred.defer(self.rebuild_data_index_deferred, old_data, new_data) # update inlinks and outlinks old_redir = old_md.get("redirect") new_redir = new_md.get("redirect") self.update_links(old_redir, new_redir) # delete config and tittle cache if self.title == ".config": cache.del_config() if self.revision == 1: cache.del_titles() return True