Skip to content
Snippets Groups Projects
views.py 12.56 KiB
"""Specific views and actions for application using the Comment entity type

:organization: Logilab
:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
_ = unicode

from itertools import count

from logilab.mtconverter import html_escape
from logilab.common.decorators import monkeypatch

from simplejson import dumps

from cubicweb.selectors import (one_line_rset, but_etype, implements,
                                has_permission, relation_possible, yes,
                                match_kwargs)
from cubicweb.view import EntityView
from cubicweb.common.uilib import rql_for_eid, cut, safe_cut
from cubicweb.common.mixins import TreeViewMixIn
from cubicweb.web import ajax_replace_url, stdmsgs, uicfg
from cubicweb.web.action import LinkToEntityAction, Action
from cubicweb.web.form import FormViewMixIn
from cubicweb.web.formwidgets import Button
from cubicweb.web.views import primary, baseviews
from cubicweb.web.component import EntityVComponent
from cubicweb.web.views.basecontrollers import JSonController


uicfg.autoform_section.tag_subject_of(('*', 'comments', '*'),  'generated')
uicfg.autoform_section.tag_object_of(('*', 'comments', '*'), 'generated')
uicfg.actionbox_appearsin_addmenu.tag_subject_of(('*', 'comments', '*'),  False)
uicfg.actionbox_appearsin_addmenu.tag_object_of(('*', 'comments', '*'), False)
uicfg.primaryview_section.tag_subject_of(('*', 'comments', '*'),  'hidden')
uicfg.primaryview_section.tag_object_of(('*', 'comments', '*'), 'hidden')

# comment views ###############################################################

class CommentPrimaryView(primary.PrimaryView):
    __select__ = implements('Comment')

    def cell_call(self, row, col):
        self.req.add_css('cubes.comment.css')
        entity = self.complete_entity(row, col)
        # display text, author and creation date
        self.w(u'<div class="comment">')
        self.w(u'<div class="commentInfo">')
        # do not try to display creator when you're not allowed to see CWUsers
        if entity.creator:
            authorlink = entity.creator.view('oneline')
            self.w(u'%s %s\n' % (self.req._('written by'), authorlink))
        self.w(self.format_date(entity.creation_date))
        # commented object
        if entity.comments:
            self.w(u",  %s " % self.req._('comments'))
            entity.comments[0].view('oneline', w=self.w)
            self.w(u"\n")
        # don't include responses in this view, since the comment section
        # component will display them
        self.w(u'</div>\n')
        self.w(u'<div class="commentBody">%s</div>\n'
               % entity.printable_value('content'))
        # XXX attribute generated_by added by email component
        if hasattr(self, 'generated_by') and self.generated_by:
            gen = self.generated_by[0]
            link = '<a href="%s">%s</a>' % (gen.absolute_url(),
                                            gen.dc_type().lower())
            txt = self.req._('this comment has been generated from this %s') % link
            self.w(u'<div class="commentBottom">%s</div>\n' % txt)
        self.w(u'</div>\n')



class CommentSecondaryView(baseviews.SecondaryView):
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        entity = self.entity(row, col)
        root = entity.root()
        self.w(u'[<a href="%s">#%s</a>] '
               % (html_escape(root.absolute_url()), root.eid))
        maxsize = self.req.property_value('navigation.short-line-size')
        maxsize = maxsize - len(str(root.eid))
        content = entity.printable_value('content', format='text/plain')
        content = html_escape(cut(content, maxsize))
        self.w(u'<a href="%s">#%s <i>%s</i></a>\n' % (
            html_escape(entity.absolute_url()), entity.eid, content))


class CommentOneLineView(CommentSecondaryView):
    id = 'oneline'


class CommentTreeItemView(baseviews.ListItemView):
    id = 'treeitem'
    __select__ = implements('Comment')

    def cell_call(self, row, col, **kwargs):
        _ = self.req._
        self.req.add_js('cubicweb.ajax.js')
        self.req.add_css('cubes.comment.css')
        entity = self.entity(row, col)
        action = self.vreg.select_action('reply_comment', self.req, self.rset,
                                         row=row)
        editaction = self.vreg.select_action('edit_comment', self.req, self.rset,
                                             row=row)
        if action is None:
            reply = u''
        else:
            url = ajax_replace_url('comment%sHolder' % entity.eid,
                                   rql_for_eid(entity.eid), 'inlinecomment')
            reply = ' | <a href="%s">%s</a>' % (html_escape(url),
                                                _(action.title))
        if editaction is None:
            edit = u''
        else:
            url = ajax_replace_url('comment%s' % entity.eid,
                                   rql_for_eid(entity.eid), 'editcomment')
            edit = ' | <a href="%s">%s</a>' % (html_escape(url),
                                               _(editaction.title))
        text = entity.printable_value('content')
        if not kwargs.get('full'):
            maxsize = self.req.property_value('navigation.short-line-size')
            text = safe_cut(text, maxsize)
        self.w(u'<div class="commentInfo">')
        self.w(self.format_date(entity.creation_date))
        if entity.creator:
            authorlink = entity.creator.view('oneline')
            self.w(u', %s <span class="author">%s</span> \n' % (_('written by'),
                                                                authorlink,))
        self.w(u'<span class="replyto"> %s </span>' % reply)
        self.w(u'<span class="replyto"> %s </span>' % edit)
        self.w(u'</div>\n')
        self.w(u'<div class="commentBody">%s</div>\n' % text)
        self.w(u'<div id="comment%sHolder"></div>' % entity.eid)


class CommentThreadView(TreeViewMixIn, baseviews.ListView):
    """a recursive tree view"""
    __select__ = implements('Comment')
    title = _('thread view')

    def open_item(self, entity):
        self.w(u'<li id="comment%s" class="comment">\n' % entity.eid)


# comment edition views #######################################################

class InlineCommentView(EntityView):
    id = 'inlinecomment'
    __select__ = yes() # explicit call when it makes sense

    def cell_call(self, row, col):
        entity = self.entity(row, col)
        self.wview('inlinecommentform', None, commented=entity)


class InlineEditCommentForm(FormViewMixIn, EntityView):
    id = 'editcomment'
    __select__ = implements('Comment')

    jsfunc = "processComment(%s, '%s')"
    
    def cell_call(self, row, col):
        self.comment_form(self.entity(row, col))

    def comment_form(self, commented, newcomment=None):
        self.req.add_js('cubes.comment.js')
        if newcomment is None:
            newcomment = commented
        # hack to avoid tabindex conflicts caused by Ajax requests
        self.req.next_tabindex = count(20).next
        jseid = dumps(commented.eid)
        buttons = [Button(onclick=self.jsfunc % (jseid, 'add_comment')),
                   Button(stdmsgs.BUTTON_CANCEL,
                          onclick=self.jsfunc % (jseid, ''))]
        form = self.vreg.select_object('forms', 'edition', self.req,
                                       None,
                                       entity=newcomment,
                                       form_buttons=buttons,
                                       attrcategories=('primary',))
        self.w(u'<div id="comment%sSlot">%s</div>' % (
            commented.eid, form.form_render(display_label=False,
                                            display_relations_form=False)))


class InlineCommentForm(InlineEditCommentForm):
    id = 'inlinecommentform'
    __select__ = match_kwargs('commented') # explicit call when it makes sense

    def call(self, commented):
        self.initialize_varmaker()
        newcomment = self.vreg.etype_class('Comment')(self.req)
        newcomment.eid = self.varmaker.next()
        self.comment_form(commented, newcomment)


# comment component ###########################################################

class CommentSectionVComponent(EntityVComponent):
    """a component to display a <div> html section including comments
    related to an object
    """
    id = 'commentsection'
    __select__ = (EntityVComponent.__select__
                  & relation_possible('comments', 'object', 'Comment'))

    context = 'navcontentbottom'

    def cell_call(self, row, col, view=None):
        req = self.req
        req.add_js( ('cubicweb.ajax.js', 'cubes.comment.js') )
        eid = self.rset[row][col]
        self.w(u'<div id="%s" class="%s" cubicweb:rooteid="%s">' % (
            self.div_id(), self.div_class(), eid))
        action = self.vreg.select_action('reply_comment', req, self.rset, row=0)
        if action is not None:
            url = ajax_replace_url('comment%sHolder' % eid, rql_for_eid(eid),
                                   'inlinecomment')
            reply = u' (<a href="%s">%s</a>)' % (url, req._(action.title))
            if req.use_fckeditor() and req.property_value('ui.default-text-format') == 'text/html':
                req.fckeditor_config()
        else:
            reply = u''
        rql = u'Any C,CD,CC,CCF,U,UL,US,UF ORDERBY CD WHERE C is Comment, '\
              'C comments X, C creation_date CD, C content CC, C content_format CCF, ' \
              'C created_by U?, U login UL, U firstname UF, U surname US, X eid %(x)s'
        rset = req.execute(rql, {'x': eid}, 'x')
        if rset.rowcount:
            self.w(u'<h4>%s</h4>%s' % (req._('Comment_plural'), reply))
            # XXX check registration is open. Also, propose to login.
            #if self.vreg.config['anonymous-user'] == self.req.user.login:
            #    self.w(u' (<a href="/?vid=register">register to comment</a>)')
            self.w(u'<div id="comment%sHolder"></div>' % eid)
            self.w(u'<ul class="comment">')
            for i in xrange(rset.rowcount):
                self.wview('tree', rset, row=i, full=True)
            self.w(u'</ul>')
        elif reply:
            self.w(reply)
            # XXX check registration is open. Also, propose to login.
            #if self.vreg.config['anonymous-user'] == self.req.user.login:
            #    self.w(u' (<a href="/?vid=register">register to comment</a>)')
            self.w(u'<div id="comment%sHolder"></div>' % eid)
        self.w(u'</div>')


# comment actions #############################################################

class ReplyCommentAction(LinkToEntityAction):
    id = 'reply_comment'
    __select__ = LinkToEntityAction.__select__ & implements('Comment')

    etype = 'Comment'
    rtype = 'comments'
    target = 'subject'

    title = _('reply to this comment')
    category = 'hidden'
    order = 111

    def url(self):
        rset = self.rset
        comment = rset.get_entity(self.row or 0, self.col or 0)
        linkto = '%s:%s:%s' % (self.rtype, comment.eid, self.target)
        return self.build_url(vid='creation', etype=self.etype,
                              __linkto=linkto,
                              __redirectpath=comment.root().rest_path(),
                              __redirectvid=self.req.form.get('vid', ''))


class AddCommentAction(LinkToEntityAction):
    """add comment is like reply for everything but Comment"""
    id = 'reply_comment'
    __select__ = LinkToEntityAction.__select__ & but_etype('Comment')

    etype = 'Comment'
    rtype = 'comments'
    role = 'object'

    title = _('add comment')
    category = 'hidden'
    order = 111


class EditCommentAction(Action):
    id = 'edit_comment'
    __select__ = one_line_rset() & implements('Comment') & has_permission('update')

    title = _('edit comment')
    category = 'hidden'
    order = 110

    def url(self):
        return self.build_url(rql=self.rset.printable_rql(), vid='edition')


# add some comments related methods to the Jsoncontroller #####################

@monkeypatch(JSonController)
def js_add_comment(self, commented, text, format):
    self.req.execute('INSERT Comment C: C comments X, C content %(text)s, '
                     'C content_format %(format)s  WHERE X eid %(x)s',
                     {'format' : format, 'text' : text, 'x' : commented}, 'x')

@monkeypatch(JSonController)
def js_edit_comment(self, comment, text, format):
    self.req.execute('SET C content %(text)s, C content_format %(format)s '
                     'WHERE C eid %(x)s',
                     {'format' : format, 'text' : text, 'x' : comment}, 'x')