view src/pages/common/blog/page_meta.py @ 1025:36e5945d064c

pages (common/blog): if "reverse=1" is set in URL query, items will be reversed
author Goffi <goffi@goffi.org>
date Sun, 21 Jan 2018 20:50:49 +0100
parents 2ae3b6291456
children 38cfee0eed48
line wrap: on
line source

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from libervia.server.constants import Const as C
from twisted.words.protocols.jabber import jid
from twisted.internet import defer
from sat.tools.common import data_objects
from libervia.server import session_iface
from sat.core.i18n import _
from sat.core.log import getLogger
import urllib
log = getLogger('pages/common/blog')

"""generic blog (with service/node provided)"""
name = u'blog'
template = u"blog/articles.html"
uri_handlers = {(u'pubsub', u'microblog'): 'microblog_uri'}


def microblog_uri(self, uri_data):
    service = urllib.quote_plus(uri_data[u'path'])
    node = urllib.quote_plus(uri_data[u'node'])
    return service + u'/' + node


def parse_url(self, request):
    """URL is /[service]/[node]/[filter_keyword]/[item]|[other]

    if [node] is '@', default namespace is used
    if a value is unset, default one will be used
    keyword can be one of:
        id: next value is a item id
        tag: next value is a blog tag
    """
    data = self.getRData(request)

    try:
        service = self.nextPath(request)
    except IndexError:
        data['service'] = u''
    else:
        try:
            data[u"service"] = jid.JID(service)
        except Exception:
            log.warning(_(u"bad service entered: {}").format(service))
            self.pageError(request, C.HTTP_BAD_REQUEST)

    try:
        data['node'] = self.nextPath(request)
    except IndexError:
        data['node'] = u''
    else:
        if data['node'] == u'@':
            data['node'] = u''

    try:
        filter_kw = data['filter_keyword'] = self.nextPath(request)
    except IndexError:
        pass
    else:
        if filter_kw == u'id':
            try:
                data[u'item'] = self.nextPath(request)
            except IndexError:
                self.pageError(request, C.HTTP_BAD_REQUEST)
        elif filter_kw == u'tag':
            try:
                data[u'tag'] = self.nextPath(request)
            except IndexError:
                self.pageError(request, C.HTTP_BAD_REQUEST)
        else:
            # invalid filter keyword
            log.warning(_(u"invalid filter keyword: {filter_kw}").format(filter_kw=filter_kw))
            self.pageError(request, C.HTTP_BAD_REQUEST)


@defer.inlineCallbacks
def appendComments(self, blog_items, identities, profile):
    for blog_item in blog_items:
        if identities is not None:
            author = blog_item.author_jid
            if author not in identities:
                identities[author] = yield self.host.bridgeCall(u'identityGet', author, profile)
        for comment_data in blog_item.comments:
            service = comment_data[u'service']
            node = comment_data[u'node']
            try:
                comments_data = yield self.host.bridge.mbGet(
                                      service,
                                      node,
                                      C.NO_LIMIT,
                                      [],
                                      {},
                                      profile)
            except Exception as e:
                log.warning(_(u"Can't get comments at {node} (service: {service}): {msg}").format(
                    service=service,
                    node=node,
                    msg=e))
                continue

            comments = data_objects.BlogItems(comments_data)
            blog_item.appendCommentsItems(comments)
            yield appendComments(self, comments, identities, profile)

@defer.inlineCallbacks
def getBlogData(self, request, service, node, item_id, extra, profile):
    try:
        if item_id:
            items_id = [item_id]
        else:
            items_id = []
        blog_data = yield self.host.bridge.mbGet(
                              service.userhost(),
                              node,
                              C.NO_LIMIT,
                              items_id,
                              extra,
                              profile)
    except Exception as e:
        # FIXME: need a better way to test errors in bridge errback
        if u"forbidden" in unicode(e):
            self.pageError(request, 401)
        else:
            raise e

    items = data_objects.BlogItems(blog_data)
    defer.returnValue((blog_data, items))

@defer.inlineCallbacks
def prepare_render(self, request):
    data = self.getRData(request)
    # if the comments are not explicitly hidden, we show them
    service, node, item_id, show_comments = data.get(u'service', u''), data.get(u'node', u''), data.get(u'item'), data.get(u'show_comments', True)
    profile = self.getProfile(request)
    if profile is None:
        profile = C.SERVICE_PROFILE

    params = self.getAllPostedData(request, multiple=False)
    if item_id:
        extra = {}
    else:
        extra = {u'rsm_max': u'10'}
        if u'after' in params:
            extra[u'rsm_after'] = params[u'after']
        elif u'before' in params:
            extra[u'rsm_before'] = params[u'before']
        tag = data.get('tag')
        if tag:
            extra[u'mam_filter_{}'.format(C.MAM_FILTER_CATEGORY)] = tag

    blog_data, items = yield getBlogData(self, request, service, node, item_id, extra, profile)
    template_data = request.template_data
    if items:
        if not item_id:
            last_id = items[-1].id
            template_data['older_url'] = self.getParamURL(request, after=last_id)
            if u'before' in params or u'after' in params:
                first_id = items[0].id
                template_data['newer_url']  = self.getParamURL(request, before=first_id)
    else:
        if item_id:
            # if item id has been specified in URL and it's not found,
            # we must return an error
            self.pageError(request, C.HTTP_NOT_FOUND)

        # no items, we have requested items before last post, or blog is empty
        extra = {u'rsm_max': u'10'}
        blog_data, items = yield getBlogData(self, request, service, node, None, extra, profile)
        if items:
            last_id = items[-1].id
            template_data['older_url'] = self.getParamURL(request, after=last_id)

    identities = template_data[u'identities'] = self.host.getSessionData(request, session_iface.ISATSession).identities

    if show_comments:
        yield appendComments(self, items, identities, profile)

    blog_view = self.getPageByName(u'blog_view')
    template_data[u'items'] = data[u'items'] = items
    if request.args.get('reverse') == ['1']:
        template_data[u'items'].items.reverse()
    template_data[u'item_http_uri'] = items_http_uri = {}
    template_data[u'tags_http_uri'] = tags_http_uri = {}
    for item in items:
        service_s = service.full()
        items_http_uri[item.id] = self.host.getExtBaseURL(request, blog_view.getURL(service_s, node or '@', u'id', item.id))
        for tag in item.tags:
            if tag not in tags_http_uri:
                tags_http_uri[tag] = self.host.getExtBaseURL(request, blog_view.getURL(service_s, node or '@', u'tag', tag))
    template_data[u'allow_commenting'] = data.get(u'allow_commenting', False)


@defer.inlineCallbacks
def on_data_post(self, request):
    profile = self.getProfile(request)
    if profile is None:
        self.pageError(request, C.HTTP_UNAUTHORIZED)
    type_ = self.getPostedData(request, u'type')
    if type_ == u'comment':
        service, node, body = self.getPostedData(request, (u'service', u'node', u'body'))

        if not body:
            self.pageError(request, C.HTTP_BAD_REQUEST)
        comment_data = {u"content": body}
        try:
            yield self.host.bridge.mbSend(service, node, comment_data, profile)
        except Exception as e:
            if u"forbidden" in unicode(e):
                self.pageError(request, 401)
            else:
                raise e
    else:
        log.warning(_(u"Unhandled data type: {}").format(type_))