Exemple #1
0
class ThreadsController(BaseController):
    __metaclass__=h.ProxiedAttrMeta
    M=h.attrproxy('_discussion_controller', 'M')
    W=h.attrproxy('_discussion_controller', 'W')
    ThreadController=h.attrproxy('_discussion_controller', 'ThreadController')
    PostController=h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController=h.attrproxy('_discussion_controller', 'AttachmentController')

    def __init__(self, discussion_controller):
        self._discussion_controller = discussion_controller

    @expose()
    def _lookup(self, id=None, *remainder):
        if id:
            id=unquote(id)
            return self.ThreadController(self._discussion_controller, id), remainder
        else:
            raise exc.HTTPNotFound()
Exemple #2
0
class ModerationController(BaseController):
    __metaclass__ = h.ProxiedAttrMeta
    PostModel = M.Post
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.discussion, 'moderate')

    def __init__(self, discussion_controller):
        self._discussion_controller = discussion_controller

    @LazyProperty
    def discussion(self):
        return self._discussion_controller.discussion

    @h.vardec
    @expose('jinja:allura:templates/discussion/moderate.html')
    @validate(pass_validator)
    def index(self, **kw):
        kw = WidgetConfig.post_filter.validate(kw, None)
        page = kw.pop('page', 0)
        limit = kw.pop('limit', 50)
        status = kw.pop('status', 'pending')
        flag = kw.pop('flag', None)
        c.post_filter = WidgetConfig.post_filter
        c.moderate_posts = WidgetConfig.moderate_posts
        query = dict(discussion_id=self.discussion._id)
        if status != '-':
            query['status'] = status
        if flag:
            query['flags'] = {'$gte': int(flag)}
        q = self.PostModel.query.find(query)
        count = q.count()
        if not page:
            page = 0
        page = int(page)
        limit = int(limit)
        q = q.skip(page)
        q = q.limit(limit)
        pgnum = (page // limit) + 1
        pages = (count // limit) + 1
        return dict(discussion=self.discussion,
                    posts=q,
                    page=page,
                    limit=limit,
                    status=status,
                    flag=flag,
                    pgnum=pgnum,
                    pages=pages)

    @h.vardec
    @expose()
    @require_post()
    def save_moderation(self,
                        post=[],
                        delete=None,
                        spam=None,
                        approve=None,
                        **kw):
        for p in post:
            if 'checked' in p:
                posted = self.PostModel.query.get(full_slug=p['full_slug'])
                if posted:
                    if delete:
                        posted.delete()
                        # If we just deleted the last post in the
                        # thread, delete the thread.
                        if posted.thread and posted.thread.num_replies == 0:
                            posted.thread.delete()
                    elif spam and posted.status != 'spam':
                        posted.spam()
                    elif approve and posted.status != 'ok':
                        posted.status = 'ok'
                        posted.thread.num_replies += 1
        redirect(request.referer)
Exemple #3
0
class PostController(BaseController):
    __metaclass__ = h.ProxiedAttrMeta
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.post, 'read')

    def __init__(self, discussion_controller, thread, slug):
        self._discussion_controller = discussion_controller
        self.thread = thread
        self._post_slug = slug
        self.attachment = DiscussionAttachmentsController(self.post)

    @LazyProperty
    def post(self):
        result = self.M.Post.query.find(dict(slug=self._post_slug)).all()
        for p in result:
            if p.thread_id == self.thread._id: return p
        if result:
            redirect(result[0].url())
        else:
            redirect('..')

    @h.vardec
    @expose('jinja:allura:templates/discussion/post.html')
    @validate(pass_validator)
    @utils.AntiSpam.validate('Spambot protection engaged')
    def index(self, version=None, **kw):
        c.post = self.W.post
        if request.method == 'POST':
            require_access(self.post, 'moderate')
            post_fields = self.W.edit_post.to_python(kw, None)
            file_info = post_fields.pop('file_info', None)
            if hasattr(file_info, 'file'):
                self.post.attach(file_info.filename,
                                 file_info.file,
                                 content_type=file_info.type,
                                 post_id=self.post._id,
                                 thread_id=self.post.thread_id,
                                 discussion_id=self.post.discussion_id)
            for k, v in post_fields.iteritems():
                try:
                    setattr(self.post, k, v)
                except AttributeError:
                    continue
            self.post.edit_count = self.post.edit_count + 1
            self.post.last_edit_date = datetime.utcnow()
            self.post.last_edit_by_id = c.user._id
            redirect(request.referer)
        elif request.method == 'GET':
            if version is not None:
                HC = self.post.__mongometa__.history_class
                ss = HC.query.find({
                    'artifact_id': self.post._id,
                    'version': int(version)
                }).first()
                if not ss: raise exc.HTTPNotFound
                post = Object(ss.data,
                              acl=self.post.acl,
                              author=self.post.author,
                              url=self.post.url,
                              thread=self.post.thread,
                              reply_subject=self.post.reply_subject,
                              attachments=self.post.attachments,
                              related_artifacts=self.post.related_artifacts)
            else:
                post = self.post
            return dict(discussion=self.post.discussion, post=post)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=index)
    @utils.AntiSpam.validate('Spambot protection engaged')
    @require_post(redir='.')
    def reply(self, **kw):
        require_access(self.thread, 'post')
        kw = self.W.edit_post.to_python(kw, None)
        self.thread.post(parent_id=self.post._id, **kw)
        self.thread.num_replies += 1
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=index)
    def moderate(self, **kw):
        require_access(self.post.thread, 'moderate')
        if kw.pop('delete', None):
            self.post.delete()
            self.thread.update_stats()
        elif kw.pop('spam', None):
            self.post.status = 'spam'
            self.thread.update_stats()
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=index)
    def flag(self, **kw):
        self.W.flag_post.to_python(kw, None)
        if c.user._id not in self.post.flagged_by:
            self.post.flagged_by.append(c.user._id)
            self.post.flags += 1
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    def attach(self, file_info=None):
        require_access(self.post, 'moderate')
        if hasattr(file_info, 'file'):
            mime_type = file_info.type
            # If mime type was not passed or bogus, guess it
            if not mime_type or '/' not in mime_type:
                mime_type = utils.guess_mime_type(file_info.filename)
            self.post.attach(file_info.filename,
                             file_info.file,
                             content_type=mime_type,
                             post_id=self.post._id,
                             thread_id=self.post.thread_id,
                             discussion_id=self.post.discussion_id)
        redirect(request.referer)

    @expose()
    def _lookup(self, id, *remainder):
        id = unquote(id)
        return self.PostController(self._discussion_controller, self.thread,
                                   self._post_slug + '/' + id), remainder
Exemple #4
0
class ThreadController(BaseController):
    __metaclass__ = h.ProxiedAttrMeta
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.thread, 'read')

    def __init__(self, discussion_controller, thread_id):
        self._discussion_controller = discussion_controller
        self.discussion = discussion_controller.discussion
        self.thread = self.M.Thread.query.get(_id=thread_id)
        if not self.thread:
            raise exc.HTTPNotFound

    @expose()
    def _lookup(self, id, *remainder):
        id = unquote(id)
        return self.PostController(self._discussion_controller, self.thread,
                                   id), remainder

    @expose('jinja:allura:templates/discussion/thread.html')
    def index(self, limit=None, page=0, count=0, **kw):
        c.thread = self.W.thread
        c.thread_header = self.W.thread_header
        limit, page, start = g.handle_paging(limit, page)
        self.thread.num_views += 1
        M.session.artifact_orm_session._get(
        ).skip_mod_date = True  # the update to num_views shouldn't affect it
        count = self.thread.query_posts(page=page, limit=int(limit)).count()
        return dict(discussion=self.thread.discussion,
                    thread=self.thread,
                    page=int(page),
                    count=int(count),
                    limit=int(limit),
                    show_moderate=kw.get('show_moderate'))

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=index)
    @utils.AntiSpam.validate('Spambot protection engaged')
    def post(self, **kw):
        require_access(self.thread, 'post')
        kw = self.W.edit_post.to_python(kw, None)
        if not kw['text']:
            flash('Your post was not saved. You must provide content.',
                  'error')
            redirect(request.referer)
        file_info = kw.get('file_info', None)
        p = self.thread.add_post(**kw)
        if hasattr(file_info, 'file'):
            p.attach(file_info.filename,
                     file_info.file,
                     content_type=file_info.type,
                     post_id=p._id,
                     thread_id=p.thread_id,
                     discussion_id=p.discussion_id)
        if self.thread.artifact:
            self.thread.artifact.mod_date = datetime.utcnow()
        flash('Message posted')
        redirect(request.referer)

    @expose()
    @require_post()
    def tag(self, labels, **kw):
        require_access(self.thread, 'post')
        self.thread.labels = labels.split(',')
        redirect(request.referer)

    @expose()
    def flag_as_spam(self, **kw):
        require_access(self.thread, 'moderate')
        self.thread.spam()
        flash('Thread flagged as spam.')
        redirect(self.discussion.url())

    @without_trailing_slash
    @expose()
    @validate(
        dict(since=DateTimeConverter(if_empty=None),
             until=DateTimeConverter(if_empty=None),
             page=validators.Int(if_empty=None),
             limit=validators.Int(if_empty=None)))
    def feed(self, since=None, until=None, page=None, limit=None):
        if request.environ['PATH_INFO'].endswith('.atom'):
            feed_type = 'atom'
        else:
            feed_type = 'rss'
        title = 'Recent posts to %s' % (self.thread.subject or '(no subject)')
        feed = M.Feed.feed(dict(ref_id=self.thread.index_id()), feed_type,
                           title, self.thread.url(), title, since, until, page,
                           limit)
        response.headers['Content-Type'] = ''
        response.content_type = 'application/xml'
        return feed.writeString('utf-8')
Exemple #5
0
class ModerationController(BaseController):
    __metaclass__ = h.ProxiedAttrMeta
    PostModel = M.Post
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.discussion, 'moderate')

    def __init__(self, discussion_controller):
        self._discussion_controller = discussion_controller

    @LazyProperty
    def discussion(self):
        return self._discussion_controller.discussion

    @h.vardec
    @expose('jinja:allura:templates/discussion/moderate.html')
    @validate(pass_validator)
    def index(self, **kw):
        kw = WidgetConfig.post_filter.validate(kw, None)
        page = kw.pop('page', 0)
        limit = kw.pop('limit', 50)
        status = kw.pop('status', 'pending')
        username = kw.pop('username', None)
        flag = kw.pop('flag', None)
        c.post_filter = WidgetConfig.post_filter
        c.moderate_posts = WidgetConfig.moderate_posts
        query = dict(discussion_id=self.discussion._id, deleted=False)
        if status != '-':
            query['status'] = status
        if flag:
            query['flags'] = {'$gte': int(flag)}
        if username:
            filtered_user = User.by_username(username)
            query['author_id'] = filtered_user._id if filtered_user else None
        q = self.PostModel.query.find(query)
        count = q.count()
        limit, page, start = g.handle_paging(limit, page or 0, default=50)
        q = q.skip(start)
        q = q.limit(limit)
        pgnum = (page // limit) + 1
        pages = (count // limit) + 1
        return dict(discussion=self.discussion,
                    posts=q,
                    page=page,
                    limit=limit,
                    status=status,
                    flag=flag,
                    username=username,
                    pgnum=pgnum,
                    pages=pages)

    @h.vardec
    @expose()
    @require_post()
    def save_moderation(self,
                        post=[],
                        delete=None,
                        spam=None,
                        approve=None,
                        **kw):
        for p in post:
            if 'checked' in p:
                posted = self.PostModel.query.get(
                    _id=p['_id'],
                    # make sure nobody hacks the HTML form to moderate other
                    # posts
                    discussion_id=self.discussion._id,
                )
                if posted:
                    if delete:
                        posted.delete()
                        # If we just deleted the last post in the
                        # thread, delete the thread.
                        if posted.thread and posted.thread.num_replies == 0:
                            posted.thread.delete()
                    elif spam and posted.status != 'spam':
                        posted.spam()
                    elif approve and posted.status != 'ok':
                        posted.approve()
                        g.spam_checker.submit_ham(posted.text,
                                                  artifact=posted,
                                                  user=posted.author())
                        posted.thread.post_to_feed(posted)
        redirect(request.referer)
Exemple #6
0
class PostController(BaseController):
    __metaclass__ = h.ProxiedAttrMeta
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.post, 'read')

    def __init__(self, discussion_controller, thread, slug):
        self._discussion_controller = discussion_controller
        self.thread = thread
        self._post_slug = slug
        self.attachment = DiscussionAttachmentsController(self.post)

    @LazyProperty
    def post(self):
        post = self.M.Post.query.get(slug=self._post_slug,
                                     thread_id=self.thread._id)
        if post:
            return post
        else:
            redirect('..')

    @h.vardec
    @expose('jinja:allura:templates/discussion/post.html')
    @validate(pass_validator)
    @utils.AntiSpam.validate('Spambot protection engaged')
    def index(self, version=None, **kw):
        c.post = self.W.post
        if request.method == 'POST':
            require_access(self.post, 'moderate')
            post_fields = self.W.edit_post.to_python(kw, None)
            file_info = post_fields.pop('file_info', None)
            self.post.add_multiple_attachments(file_info)
            for k, v in post_fields.iteritems():
                try:
                    setattr(self.post, k, v)
                except AttributeError:
                    continue
            self.post.edit_count = self.post.edit_count + 1
            self.post.last_edit_date = datetime.utcnow()
            self.post.last_edit_by_id = c.user._id
            self.post.commit()
            g.director.create_activity(
                c.user,
                'modified',
                self.post,
                target=self.post.thread.artifact or self.post.thread,
                related_nodes=[self.post.app_config.project],
                tags=['comment'])
            redirect(request.referer)
        elif request.method == 'GET':
            if self.post.deleted:
                raise exc.HTTPNotFound
            if version is not None:
                HC = self.post.__mongometa__.history_class
                ss = HC.query.find({
                    'artifact_id': self.post._id,
                    'version': int(version)
                }).first()
                if not ss:
                    raise exc.HTTPNotFound
                post = Object(
                    ss.data,
                    acl=self.post.acl,
                    author=self.post.author,
                    url=self.post.url,
                    thread=self.post.thread,
                    reply_subject=self.post.reply_subject,
                    attachments=self.post.attachments,
                    related_artifacts=self.post.related_artifacts,
                    parent_security_context=lambda: None,
                )
            else:
                post = self.post
            return dict(discussion=self.post.discussion, post=post)

    def error_handler(self, *args, **kwargs):
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=error_handler)
    @utils.AntiSpam.validate('Spambot protection engaged')
    @require_post(redir='.')
    def reply(self, file_info=None, **kw):
        require_access(self.thread, 'post')
        kw = self.W.edit_post.to_python(kw, None)
        p = self.thread.add_post(parent_id=self.post._id, **kw)
        p.add_multiple_attachments(file_info)
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=error_handler)
    def moderate(self, **kw):
        require_access(self.post.thread, 'moderate')
        if kw.pop('delete', None):
            self.post.delete()
        elif kw.pop('spam', None):
            self.post.spam()
        elif kw.pop('undo', None):
            prev_status = kw.pop('prev_status', None)
            self.post.undo(prev_status)
        elif kw.pop('approve', None):
            if self.post.status != 'ok':
                self.post.approve()
                g.spam_checker.submit_ham(self.post.text,
                                          artifact=self.post,
                                          user=self.post.author())
                self.post.thread.post_to_feed(self.post)
        return dict(result='success')

    @h.vardec
    @expose()
    @require_post()
    def attach(self, file_info=None):
        require_access(self.post, 'moderate')
        self.post.add_multiple_attachments(file_info)
        redirect(request.referer)

    @expose()
    def _lookup(self, id, *remainder):
        id = unquote(id)
        return self.PostController(self._discussion_controller, self.thread,
                                   self._post_slug + '/' + id), remainder
Exemple #7
0
class ThreadController(BaseController, FeedController):
    __metaclass__ = h.ProxiedAttrMeta
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.thread, 'read')
        if self.thread.ref:
            require_access(self.thread.ref.artifact, 'read')

    def __init__(self, discussion_controller, thread_id):
        self._discussion_controller = discussion_controller
        self.discussion = discussion_controller.discussion
        self.thread = self.M.Thread.query.get(_id=thread_id)
        if not self.thread:
            raise exc.HTTPNotFound

    @expose()
    def _lookup(self, id, *remainder):
        id = unquote(id)
        return self.PostController(self._discussion_controller, self.thread,
                                   id), remainder

    @with_trailing_slash
    @expose('jinja:allura:templates/discussion/thread.html')
    def index(self, limit=None, page=0, count=0, **kw):
        c.thread = self.W.thread
        c.thread_header = self.W.thread_header
        limit, page, start = g.handle_paging(limit, page)
        self.thread.num_views += 1
        # the update to num_views shouldn't affect it
        M.session.artifact_orm_session._get().skip_mod_date = True
        M.session.artifact_orm_session._get().skip_last_updated = True
        count = self.thread.query_posts(page=page, limit=int(limit)).count()
        return dict(discussion=self.thread.discussion,
                    thread=self.thread,
                    page=int(page),
                    count=int(count),
                    limit=int(limit),
                    show_moderate=kw.get('show_moderate'))

    def error_handler(self, *args, **kwargs):
        redirect(request.referer)

    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=error_handler)
    @utils.AntiSpam.validate('Spambot protection engaged')
    def post(self, **kw):
        require_access(self.thread, 'post')
        if self.thread.ref:
            require_access(self.thread.ref.artifact, 'post')
        kw = self.W.edit_post.to_python(kw, None)
        if not kw['text']:
            flash('Your post was not saved. You must provide content.',
                  'error')
            redirect(request.referer)

        file_info = kw.get('file_info', None)
        p = self.thread.add_post(**kw)
        p.add_multiple_attachments(file_info)
        if self.thread.artifact:
            self.thread.artifact.mod_date = datetime.utcnow()
        flash('Message posted')
        redirect(request.referer)

    @expose()
    @require_post()
    def tag(self, labels, **kw):
        require_access(self.thread, 'post')
        if self.thread.ref:
            require_access(self.thread.ref.artifact, 'post')
        self.thread.labels = labels.split(',')
        redirect(request.referer)

    @expose()
    def flag_as_spam(self, **kw):
        require_access(self.thread, 'moderate')
        self.thread.spam()
        flash('Thread flagged as spam.')
        redirect(self.discussion.url())

    def get_feed(self, project, app, user):
        """Return a :class:`allura.controllers.feed.FeedArgs` object describing
        the xml feed for this controller.

        Overrides :meth:`allura.controllers.feed.FeedController.get_feed`.

        """
        return FeedArgs(
            dict(ref_id=self.thread.index_id()),
            'Recent posts to %s' % (self.thread.subject or '(no subject)'),
            self.thread.url())
Exemple #8
0
class ModerationController(
        six.with_metaclass(h.ProxiedAttrMeta, BaseController)):
    PostModel = M.Post
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.discussion, 'moderate')

    def __init__(self, discussion_controller):
        self._discussion_controller = discussion_controller

    @LazyProperty
    def discussion(self):
        return self._discussion_controller.discussion

    @h.vardec
    @expose('jinja:allura:templates/discussion/moderate.html')
    @validate(pass_validator)
    def index(self, **kw):
        kw = WidgetConfig.post_filter.validate(kw, None)
        page = kw.pop('page', 0)
        limit = kw.pop('limit', 50)
        status = kw.pop('status', 'pending')
        username = kw.pop('username', None)
        flag = kw.pop('flag', None)
        c.post_filter = WidgetConfig.post_filter
        c.moderate_posts = WidgetConfig.moderate_posts
        c.page_list = WidgetConfig.page_list
        query = dict(discussion_id=self.discussion._id, deleted=False)
        if status != '-':
            query['status'] = status
        if flag:
            query['flags'] = {'$gte': int(flag)}
        if username:
            filtered_user = User.by_username(username)
            query['author_id'] = filtered_user._id if filtered_user else None
        q = self.PostModel.query.find(query).sort('timestamp', -1)
        count = q.count()
        limit, page, start = g.handle_paging(limit, page or 0, default=50)
        q = q.skip(start)
        q = q.limit(limit)
        pgnum = (page // limit) + 1
        pages = (count // limit) + 1
        return dict(discussion=self.discussion,
                    posts=q,
                    page=page,
                    limit=limit,
                    status=status,
                    flag=flag,
                    username=username,
                    pgnum=pgnum,
                    pages=pages,
                    count=count)

    @h.vardec
    @expose()
    @require_post()
    def save_moderation(self,
                        post=[],
                        delete=None,
                        spam=None,
                        approve=None,
                        **kw):
        count = 0
        for p in post:
            posted = None
            if isinstance(p, dict):
                # regular form submit
                if 'checked' in p:
                    posted = self.PostModel.query.get(
                        _id=p['_id'],
                        # make sure nobody hacks the HTML form to moderate other
                        # posts
                        discussion_id=self.discussion._id,
                    )
            elif isinstance(p, self.PostModel):
                # called from save_moderation_bulk_user with models already
                posted = p
            else:
                raise TypeError(
                    'post list should be form fields, or Post models')

            if posted:
                if delete:
                    posted.delete()
                    # If we just deleted the last post in the
                    # thread, delete the thread.
                    if posted.thread and posted.thread.num_replies == 0:
                        count += 1
                        posted.thread.delete()
                elif spam and posted.status != 'spam':
                    count += 1
                    posted.spam()
                elif approve and posted.status != 'ok':
                    count += 1
                    posted.approve()
                    g.spam_checker.submit_ham(posted.text,
                                              artifact=posted,
                                              user=posted.author())
                    posted.thread.post_to_feed(posted)
        flash('{} {}'.format(
            h.text.plural(count, 'post', 'posts'),
            'deleted' if delete else 'marked as spam' if spam else 'approved'))
        redirect(request.referer or '/')

    @expose()
    @require_post()
    def save_moderation_bulk_user(self, username, **kw):
        # this is used by post.js as a quick way to deal with all a user's posts
        user = User.by_username(username)
        posts = self.PostModel.query.find({
            'author_id': user._id,
            'deleted': False,
            # this is what the main moderation forms does (e.g. single discussion within a forum app)
            # 'discussion_id': self.discussion._id
            # but instead want to do all discussions within this app
            'app_config_id': c.app.config._id
        })
        return self.save_moderation(posts, **kw)
Exemple #9
0
class PostController(six.with_metaclass(h.ProxiedAttrMeta, BaseController)):
    M = h.attrproxy('_discussion_controller', 'M')
    W = h.attrproxy('_discussion_controller', 'W')
    ThreadController = h.attrproxy('_discussion_controller',
                                   'ThreadController')
    PostController = h.attrproxy('_discussion_controller', 'PostController')
    AttachmentController = h.attrproxy('_discussion_controller',
                                       'AttachmentController')

    def _check_security(self):
        require_access(self.post, 'read')

    def __init__(self, discussion_controller, thread, slug):
        self._discussion_controller = discussion_controller
        self.thread = thread
        self._post_slug = slug
        self.attachment = DiscussionAttachmentsController(self.post)

    @LazyProperty
    def post(self):
        post = self.M.Post.query.get(slug=self._post_slug,
                                     thread_id=self.thread._id)
        if post:
            return post
        else:
            redirect('..')

    @h.vardec
    @expose('jinja:allura:templates/discussion/post.html')
    @validate(pass_validator)
    @utils.AntiSpam.validate('Spambot protection engaged')
    def index(self, version=None, **kw):
        c.post = self.W.post
        if request.method == 'POST':
            old_text = self.post.text
            require_access(self.post, 'moderate')
            post_fields = self.W.edit_post.to_python(
                kw, None
            )  # could raise Invalid, but doesn't seem like it ever does
            file_info = post_fields.pop('file_info', None)
            self.post.add_multiple_attachments(file_info)
            for k, v in six.iteritems(post_fields):
                try:
                    setattr(self.post, k, v)
                except AttributeError:
                    continue
            self.post.edit_count = self.post.edit_count + 1
            self.post.last_edit_date = datetime.utcnow()
            self.post.last_edit_by_id = c.user._id
            self.thread.is_spam(
                self.post)  # run spam checker, nothing to do with result yet
            self.post.commit()
            notification_tasks.send_usermentions_notification.post(
                self.post.index_id(), kw['text'], old_text)
            g.director.create_activity(
                c.user,
                'modified',
                self.post,
                target=self.post.thread.artifact or self.post.thread,
                related_nodes=[self.post.app_config.project],
                tags=['comment'])
            redirect(request.referer or '/')
        elif request.method == 'GET':
            if self.post.deleted:
                raise exc.HTTPNotFound
            if version is not None:
                HC = self.post.__mongometa__.history_class
                ss = HC.query.find({
                    'artifact_id': self.post._id,
                    'version': int(version)
                }).first()
                if not ss:
                    raise exc.HTTPNotFound

                class VersionedSnapshotTempObject(Object):
                    pass

                post = VersionedSnapshotTempObject(
                    ss.data,
                    acl=self.post.acl,
                    author=self.post.author,
                    url=self.post.url,
                    thread=self.post.thread,
                    reply_subject=self.post.reply_subject,
                    attachments=self.post.attachments,
                    related_artifacts=self.post.related_artifacts,
                    parent_security_context=lambda: None,
                    last_edit_by=lambda: self.post.last_edit_by())
            else:
                post = self.post
            return dict(discussion=self.post.discussion, post=post)

    @without_trailing_slash
    @expose('json:')
    @require_post()
    def update_markdown(self, text=None, **kw):
        if has_access(self.post, 'moderate'):
            self.post.text = text
            self.post.edit_count = self.post.edit_count + 1
            self.post.last_edit_date = datetime.utcnow()
            self.post.last_edit_by_id = c.user._id
            self.post.commit()
            g.director.create_activity(
                c.user,
                'modified',
                self.post,
                target=self.post.thread.artifact or self.post.thread,
                related_nodes=[self.post.app_config.project],
                tags=['comment'])
            return {'status': 'success'}
        else:
            return {'status': 'no_permission'}

    @expose()
    @without_trailing_slash
    def get_markdown(self):
        return self.post.text

    @expose('json:')
    @without_trailing_slash
    @require_post()
    def post_reaction(self, r, **kw):
        if c.user.is_anonymous():
            return {'error': 'no_permission'}
        status = 'ok'
        if r in utils.get_reaction_emoji_list():
            self.post.post_reaction(r, c.user)
        else:
            status = 'error'
        return dict(status=status, counts=self.post.react_counts)

    def error_handler(self, *args, **kwargs):
        redirect(request.referer or '/')

    @memorable_forget()
    @h.vardec
    @expose()
    @require_post()
    @validate(pass_validator, error_handler=error_handler)
    @utils.AntiSpam.validate('Spambot protection engaged')
    @require_post(redir='.')
    def reply(self, **kw):
        handle_post_or_reply(thread=self.thread,
                             parent_post_id=self.post._id,
                             edit_widget=self.W.edit_post,
                             rate_limit=self.rate_limit,
                             kw=kw)

    @h.vardec
    @expose('json')
    @require_post()
    @validate(pass_validator, error_handler=error_handler)
    def moderate(self, **kw):
        require_access(self.post.thread, 'moderate')
        if kw.pop('delete', None):
            self.post.delete()
        elif kw.pop('spam', None):
            self.post.spam()
        elif kw.pop('undo', None):
            prev_status = kw.pop('prev_status', None)
            if self.post.status == 'spam' and prev_status == 'ok':
                g.spam_checker.submit_ham(self.post.text,
                                          artifact=self.post,
                                          user=self.post.author())
            self.post.undo(prev_status)
        elif kw.pop('approve', None):
            if self.post.status != 'ok':
                self.post.approve()
                g.spam_checker.submit_ham(self.post.text,
                                          artifact=self.post,
                                          user=self.post.author())
                self.post.thread.post_to_feed(self.post)
        return dict(result='success')

    @h.vardec
    @expose()
    @require_post()
    def attach(self, file_info=None):
        require_access(self.post, 'moderate')
        self.post.add_multiple_attachments(file_info)
        redirect(request.referer or '/')

    @expose()
    def _lookup(self, id, *remainder):
        id = unquote(id)
        return self.PostController(self._discussion_controller, self.thread,
                                   self._post_slug + '/' + id), remainder