Example #1
0
class WikiApiController(WikiController):
    @require_oauth2_scope("wikiedit")
    @validate(VModhash(),
              pageandprevious=VWikiPageRevise(('page', 'previous'),
                                              restricted=True),
              content=nop(('content')),
              page_name=VWikiPageName('page'),
              reason=VPrintable('reason', 256, empty_error=None))
    @api_doc(api_section.wiki, uri='/api/wiki/edit', uses_site=True)
    def POST_wiki_edit(self, pageandprevious, content, page_name, reason):
        """Edit a wiki `page`"""
        page, previous = pageandprevious

        if not page:
            error = c.errors.get(('WIKI_CREATE_ERROR', 'page'))
            if error:
                self.handle_error(403, **(error.msg_params or {}))
            if not c.user._spam:
                page = WikiPage.create(c.site, page_name)
        if c.user._spam:
            error = _("You are doing that too much, please try again later.")
            self.handle_error(415, 'SPECIAL_ERRORS', special_errors=[error])

        renderer = RENDERERS_BY_PAGE.get(page.name, 'wiki')
        if renderer in ('wiki', 'reddit'):
            content = VMarkdown(('content'), renderer=renderer).run(content)

        # Use the raw POST value as we need to tell the difference between
        # None/Undefined and an empty string.  The validators use a default
        # value with both of those cases and would need to be changed.
        # In order to avoid breaking functionality, this was done instead.
        previous = previous._id if previous else request.POST.get('previous')
        try:
            # special validation methods
            if page.name == 'config/stylesheet':
                css_errors, parsed = c.site.parse_css(content, verify=False)
                if g.css_killswitch:
                    self.handle_error(403, 'STYLESHEET_EDIT_DENIED')
                if css_errors:
                    error_items = [CssError(x).message for x in css_errors]
                    self.handle_error(415,
                                      'SPECIAL_ERRORS',
                                      special_errors=error_items)
            elif page.name == "config/automoderator":
                try:
                    rules = Ruleset(content)
                except ValueError as e:
                    error_items = [e.message]
                    self.handle_error(415,
                                      "SPECIAL_ERRORS",
                                      special_errors=error_items)

            # special saving methods
            if page.name == "config/stylesheet":
                c.site.change_css(content, parsed, previous, reason=reason)
            else:
                try:
                    page.revise(content, previous, c.user._id36, reason=reason)
                except ContentLengthError as e:
                    self.handle_error(403,
                                      'CONTENT_LENGTH_ERROR',
                                      max_length=e.max_length)

                # continue storing the special pages as data attributes on the subreddit
                # object. TODO: change this to minimize subreddit get sizes.
                if page.special and page.name in ATTRIBUTE_BY_PAGE:
                    setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content)
                    c.site._commit()

                if page.special or c.is_wiki_mod:
                    description = modactions.get(page.name,
                                                 'Page %s edited' % page.name)
                    ModAction.create(c.site,
                                     c.user,
                                     "wikirevise",
                                     details=description,
                                     description=reason)
        except ConflictException as e:
            self.handle_error(409,
                              'EDIT_CONFLICT',
                              newcontent=e.new,
                              newrevision=page.revision,
                              diffcontent=e.htmldiff)
        return json.dumps({})

    @require_oauth2_scope("modwiki")
    @validate(VModhash(),
              VWikiModerator(),
              page=VWikiPage('page'),
              act=VOneOf('act', ('del', 'add')),
              user=VExistingUname('username'))
    @api_doc(api_section.wiki,
             uri='/api/wiki/alloweditor/{act}',
             uses_site=True,
             uri_variants=[
                 '/api/wiki/alloweditor/%s' % act for act in ('del', 'add')
             ])
    def POST_wiki_allow_editor(self, act, page, user):
        """Allow/deny `username` to edit this wiki `page`"""
        if not user:
            self.handle_error(404, 'UNKNOWN_USER')
        elif act == 'del':
            page.remove_editor(user._id36)
        elif act == 'add':
            page.add_editor(user._id36)
        else:
            self.handle_error(400, 'INVALID_ACTION')
        return json.dumps({})

    @validate(
        VModhash(),
        VAdmin(),
        pv=VWikiPageAndVersion(('page', 'revision')),
        deleted=VBoolean('deleted'),
    )
    def POST_wiki_revision_delete(self, pv, deleted):
        page, revision = pv
        if not revision:
            self.handle_error(400, 'INVALID_REVISION')
        if deleted and page.revision == str(revision._id):
            self.handle_error(400, 'REVISION_IS_CURRENT')
        revision.admin_deleted = deleted
        revision._commit()
        return json.dumps({'status': revision.admin_deleted})

    @require_oauth2_scope("modwiki")
    @validate(VModhash(),
              VWikiModerator(),
              pv=VWikiPageAndVersion(('page', 'revision')))
    @api_doc(api_section.wiki, uri='/api/wiki/hide', uses_site=True)
    def POST_wiki_revision_hide(self, pv):
        """Toggle the public visibility of a wiki page revision"""
        page, revision = pv
        if not revision:
            self.handle_error(400, 'INVALID_REVISION')
        return json.dumps({'status': revision.toggle_hide()})

    @require_oauth2_scope("modwiki")
    @validate(VModhash(),
              VWikiModerator(),
              pv=VWikiPageAndVersion(('page', 'revision')))
    @api_doc(api_section.wiki, uri='/api/wiki/revert', uses_site=True)
    def POST_wiki_revision_revert(self, pv):
        """Revert a wiki `page` to `revision`"""
        page, revision = pv
        if not revision:
            self.handle_error(400, 'INVALID_REVISION')
        content = revision.content
        reason = 'reverted back %s' % timesince(revision.date)
        if page.name == 'config/stylesheet':
            css_errors, parsed = c.site.parse_css(content)
            if css_errors:
                self.handle_error(403, 'INVALID_CSS')
            c.site.change_css(content,
                              parsed,
                              prev=None,
                              reason=reason,
                              force=True)
        else:
            try:
                page.revise(content,
                            author=c.user._id36,
                            reason=reason,
                            force=True)

                # continue storing the special pages as data attributes on the subreddit
                # object. TODO: change this to minimize subreddit get sizes.
                if page.special:
                    setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content)
                    c.site._commit()
            except ContentLengthError as e:
                self.handle_error(403,
                                  'CONTENT_LENGTH_ERROR',
                                  max_length=e.max_length)
        return json.dumps({})

    def pre(self):
        WikiController.pre(self)
        c.render_style = 'api'
        set_extension(request.environ, 'json')
Example #2
0
File: wiki.py Project: wal-f/reddit
class WikiApiController(WikiController):
    @validate(VModhash(),
              pageandprevious=VWikiPageRevise(('page', 'previous'), restricted=True),
              content=VMarkdown(('content'), renderer='wiki'),
              page_name=VWikiPageName('page'),
              reason=VPrintable('reason', 256))
    @api_doc(api_section.wiki, uri='/api/wiki/edit')
    def POST_wiki_edit(self, pageandprevious, content, page_name, reason):
        page, previous = pageandprevious

        if not page:
            error = c.errors.get(('WIKI_CREATE_ERROR', 'page'))
            if error:
                self.handle_error(403, **(error.msg_params or {}))
            if not c.user._spam:
                page = WikiPage.create(c.site, page_name)
        if c.user._spam:
            error = _("You are doing that too much, please try again later.")
            self.handle_error(415, 'SPECIAL_ERRORS', special_errors=[error])
        # Use the raw POST value as we need to tell the difference between
        # None/Undefined and an empty string.  The validators use a default
        # value with both of those cases and would need to be changed.
        # In order to avoid breaking functionality, this was done instead.
        previous = previous._id if previous else request.post.get('previous')
        try:
            if page.name == 'config/stylesheet':
                report, parsed = c.site.parse_css(content, verify=False)
                if report is None:  # g.css_killswitch
                    self.handle_error(403, 'STYLESHEET_EDIT_DENIED')
                if report.errors:
                    error_items = [x.message for x in sorted(report.errors)]
                    self.handle_error(415, 'SPECIAL_ERRORS', special_errors=error_items)
                c.site.change_css(content, parsed, previous, reason=reason)
            else:
                try:
                    page.revise(content, previous, c.user._id36, reason=reason)
                except ContentLengthError as e:
                    self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length)

                # continue storing the special pages as data attributes on the subreddit
                # object. TODO: change this to minimize subreddit get sizes.
                if page.special:
                    setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content)
                    setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", str(page.revision))
                    c.site._commit()

                if page.special or c.is_wiki_mod:
                    description = modactions.get(page.name, 'Page %s edited' % page.name)
                    ModAction.create(c.site, c.user, 'wikirevise', details=description)
        except ConflictException as e:
            self.handle_error(409, 'EDIT_CONFLICT', newcontent=e.new, newrevision=page.revision, diffcontent=e.htmldiff)
        return json.dumps({})

    @validate(VModhash(),
              VWikiModerator(),
              page=VWikiPage('page'),
              act=VOneOf('act', ('del', 'add')),
              user=VExistingUname('username'))
    @api_doc(api_section.wiki, uri='/api/wiki/alloweditor/:act')
    def POST_wiki_allow_editor(self, act, page, user):
        if not user:
            self.handle_error(404, 'UNKNOWN_USER')
        elif act == 'del':
            page.remove_editor(user._id36)
        elif act == 'add':
            page.add_editor(user._id36)
        else:
            self.handle_error(400, 'INVALID_ACTION')
        return json.dumps({})

    @validate(VModhash(),
              VWikiModerator(),
              pv=VWikiPageAndVersion(('page', 'revision')))
    @api_doc(api_section.wiki, uri='/api/wiki/hide')
    def POST_wiki_revision_hide(self, pv):
        page, revision = pv
        if not revision:
            self.handle_error(400, 'INVALID_REVISION')
        return json.dumps({'status': revision.toggle_hide()})

    @validate(VModhash(),
              VWikiModerator(),
              pv=VWikiPageAndVersion(('page', 'revision')))
    @api_doc(api_section.wiki, uri='/api/wiki/revert')
    def POST_wiki_revision_revert(self, pv):
        page, revision = pv
        if not revision:
            self.handle_error(400, 'INVALID_REVISION')
        content = revision.content
        reason = 'reverted back %s' % timesince(revision.date)
        if page.name == 'config/stylesheet':
            report, parsed = c.site.parse_css(content)
            if report.errors:
                self.handle_error(403, 'INVALID_CSS')
            c.site.change_css(content, parsed, prev=None, reason=reason, force=True)
        else:
            try:
                page.revise(content, author=c.user._id36, reason=reason, force=True)

                # continue storing the special pages as data attributes on the subreddit
                # object. TODO: change this to minimize subreddit get sizes.
                if page.special:
                    setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content)
                    setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", page.revision)
                    c.site._commit()
            except ContentLengthError as e:
                self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length)
        return json.dumps({})

    def pre(self):
        WikiController.pre(self)
        c.render_style = 'api'
        set_extension(request.environ, 'json')