Example #1
0
    def _unpublish(self, save):
        if self.published_at is None:
            return

        cache.del_rendered_body(self.title)
        cache.del_hashbangs(self.title)
        if self.newer_title:
            cache.del_rendered_body(self.newer_title)
            cache.del_hashbangs(self.newer_title)
        if self.older_title:
            cache.del_rendered_body(self.older_title)
            cache.del_hashbangs(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()
Example #2
0
    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_rendered_body(self.title)
        cache.del_hashbangs(self.title)
        if self.newer_title:
            cache.del_rendered_body(self.newer_title)
            cache.del_hashbangs(self.newer_title)
        if self.older_title:
            cache.del_rendered_body(self.older_title)
            cache.del_hashbangs(self.older_title)
Example #3
0
    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
Example #4
0
    def update_links(self, old_redir, new_redir):
        """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_rendered_body(page.title)
                    cache.del_hashbangs(page.title)

                target.add_inlinks(source.inlinks[rel], rel)
                del source.inlinks[rel]

            source.put()
            cache.del_rendered_body(source.title)
            cache.del_hashbangs(source.title)
            target.put()
            cache.del_rendered_body(target.title)
            cache.del_hashbangs(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_rendered_body(page.title)
                        cache.del_hashbangs(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_rendered_body(page.title)
                    cache.del_hashbangs(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_rendered_body(page.title)
                        cache.del_hashbangs(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()
Example #5
0
    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)
        cache.del_hashbangs(self.title)

        # update body
        old_md = self.metadata
        new_md = PageOperationMixin.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(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.')
        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)

        # 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