Skip to content
Snippets Groups Projects
views.py 13.8 KiB
Newer Older
Nicolas Chauvat's avatar
Nicolas Chauvat committed
"""Specific views and actions for application using the Comment entity type

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

from itertools import count

from logilab.mtconverter import html_escape

from simplejson import dumps

from cubicweb.common.uilib import rql_for_eid, cut, safe_cut, ajax_replace_url
from cubicweb.common.view import EntityView
from cubicweb.common.mixins import TreeViewMixIn
sylvain thenault's avatar
sylvain thenault committed
from cubicweb.common.selectors import (match_kwargs, searchstate_accept,
                                    one_line_rset, accept)
Nicolas Chauvat's avatar
Nicolas Chauvat committed

from cubicweb.web.views import baseviews
from cubicweb.web import stdmsgs

_ = unicode

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

class CommentPrimaryView(baseviews.PrimaryView):
    accepts = ('Comment',)
    
    def cell_call(self, row, col):
        self.req.add_css('cubes.comment.css')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        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 EUsers
        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:
            origlink = '<a href="%s">%s</a>' % (self.generated_by[0].absolute_url(),
                                                self.generated_by[0].dc_type().lower())
            txt = self.req._('this comment has been generated from this %s') % origlink
            self.w(u'<div class="commentBottom">%s</div>\n' % txt)
        self.w(u'</div>\n')

    

class CommentSecondaryView(baseviews.SecondaryView):
    accepts = ('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):

    accepts = ('Comment',)
    id = 'treeitem'
    
    def cell_call(self, row, col, **kwargs):
        _ = self.req._
        self.req.add_js('cubicweb.ajax.js')
        self.req.add_css('cubes.comment.css')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        entity = self.entity(row, col)
        diem = self.format_date(entity.creation_date)
        action = self.vreg.select_action('reply_comment', self.req, self.rset, row)
        editaction = self.vreg.select_action('edit_comment', self.req, self.rset, 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"""
    accepts = ('Comment',)
    title = _('thread view')
        
    def open_item(self, entity):
        self.w(u'<li id="comment%s" class="comment">\n' % entity.eid)


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

from cubicweb.web.views import baseforms

class InlineCommentView(EntityView):
    id = 'inlinecomment'
    accepts = ('Any',) # XXX should support the comments relation
    requires = ('Comment',)
sylvain thenault's avatar
sylvain thenault committed
    __selectors__ = (one_line_rset, accept)
Nicolas Chauvat's avatar
Nicolas Chauvat committed

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

class InlineCommentForm(baseforms.CreationForm):
    id = 'inlinecommentform'
    title = None # hidden
    requires = ('Comment',)
    accepts = ('Comment',)
sylvain thenault's avatar
sylvain thenault committed
    __selectors__ = (match_kwargs,)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
    expected_kwargs = ('commented',)
    
    EDITION_BODY = u"""
Laure Bourgois's avatar
Laure Bourgois committed
    
Nicolas Chauvat's avatar
Nicolas Chauvat committed
 <div id="comment%(eid)sSlot">%(attrform)s
 <div id="comment%(eid)sbbar"><button onclick="%(onclick)s" tabindex="%(tabindex1)s">%(validate)s</button>
  <button onclick="%(oncancelclick)s" tabindex="%(tabindex2)s">%(cancel)s</button>
 
Nicolas Chauvat's avatar
Nicolas Chauvat committed
 </div>
</div>
"""
    def call(self, commented):
        self.req.add_js('cubicweb.ajax.js','cubes.comment.js')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        newcomment = self.vreg.etype_class('Comment')(self.req, None, None)
        newcomment.eid = 'INLINE'
        # hack to avoid tabindex conflicts caused by Ajax requests
        self.req.next_tabindex = count(20).next
        self._hiddens = []
        attrform = self.attributes_form(newcomment, {})
        onclick = html_escape("processComment(%s, 'add_comment')" % dumps(commented.eid))
        oncancelclick = html_escape("processComment(%s, '')" % dumps(commented.eid))
        self.w(self.EDITION_BODY % dict(attrform=attrform, onclick=onclick, oncancelclick=oncancelclick,
Nicolas Chauvat's avatar
Nicolas Chauvat committed
                                        eid=commented.eid,
                                        tabindex1=self.req.next_tabindex(),
                                        tabindex2=self.req.next_tabindex(),
                                        validate=self.req._(stdmsgs.BUTTON_OK),
                                        cancel=self.req._(stdmsgs.BUTTON_CANCEL)))

    def editable_attributes(self, entity):
        return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary'), 'add')
                if rschema.is_final() and rschema != 'eid']

 # don't show comments relation in edition forms
try:
    baseforms.EditionForm.skip_relations.add('comments')
except AttributeError:
    pass # XXX bw compat cubicweb < 2.49

class InlineEditCommentForm(baseforms.EditionForm):
    id = 'editcomment'
    accepts = ('Comment',)
    EDITION_BODY = u"""
 <div id="comment%(eid)sSlot">%(attrform)s
  <div>
   <button onclick="%(onclick)s" tabindex="%(tabindex1)s">%(validate)s</button>
   <button onclick="%(oncancelclick)s" tabindex="%(tabindex2)s">%(cancel)s</button>
  </div>
</div>
"""
    def cell_call(self, row, col):
        self.req.add_js( ('cubicweb.ajax.js', 'cubes.comment.js') )
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        # hack to avoid tabindex conflicts caused by Ajax request
        self.req.next_tabindex = count(20).next
        comment = self.entity(row, col)
        self._hiddens = []
        attrform = self.attributes_form(comment, {'tab_class': ''})
        onclick = html_escape("processComment(%s, 'edit_comment')" % dumps(comment.eid))
        oncancelclick = html_escape("processComment(%s, '')" % dumps(comment.eid))
        self.w(self.EDITION_BODY % dict(attrform=attrform, onclick=onclick, oncancelclick=oncancelclick,
                                        eid=comment.eid,
                                        tabindex1=self.req.next_tabindex(),
                                        tabindex2=self.req.next_tabindex(),
                                        validate=self.req._(stdmsgs.BUTTON_OK),
                                        cancel=self.req._(stdmsgs.BUTTON_CANCEL)))
                                        
    def editable_attributes(self, entity):
        return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary'), 'add')
                  if rschema.is_final() and rschema != 'eid']

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

from cubicweb.web.component import EntityVComponent

class CommentSectionVComponent(EntityVComponent):
    """a component to display a <div> html section including comments
    related to an object
    """
    id = 'commentsection'
    etype = 'Comment'
    rtype = 'comments'
    target = 'subject'
    context = 'navcontentbottom'

    def call(self, view=None, orderby='diem'):
        req = self.req
        req.add_js( ('cubicweb.ajax.js', 'cubes.comment.js') )
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        eid = self.rset[0][0]
        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.property_value('ui.fckeditor') and \
                   req.property_value('ui.default-text-format') == 'text/html':
                req.add_js('fckeditor.js')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
                req.fckeditor_config()
        else:
            reply = u''        
        if orderby == 'author':
            rql = u'Any C,CD,CC,CCF,U,UL,US,UF ORDERBY UL 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'
        else: # orderby == 'diem'
            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')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        if rset.rowcount:
            self.w(u'<h4>%s</h4>%s' % (req._('Comment_plural'), reply))
            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>')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
            self.w(reply)
            self.w(u'<div id="comment%sHolder"></div>' % eid)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
        self.w(u'</div>')

baseviews.PRIMARY_SKIP_RELS.add('comments') # displayed by the above component

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

from cubicweb.web.action import (LinkToEntityAction, LinkToEntityAction2, 
                              EntityAction)

class ReplyCommentAction(LinkToEntityAction):
    category = 'hidden'
    accepts = ('Comment',)
    etype = 'Comment'
    rtype = 'comments'
    target = 'subject'
    order = 111
    
    id = 'reply_comment'
    title = _('reply to this comment')

    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(LinkToEntityAction2):
    """add comment is like reply for everything but Comment"""
    category = 'hidden'
    accepts = ('Any',)
    etype = 'Comment'
    rtype = 'comments'
    target = 'subject'
    order = 111
    
    id = 'reply_comment'
    title = _('add comment')


class EditCommentAction(EntityAction):
    category = 'hidden'
sylvain thenault's avatar
sylvain thenault committed
    __selectors__ = (one_line_rset, searchstate_accept)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
    accepts = ('Comment',)
    etype = 'Comment'
    schema_action = 'update'
    order = 110
    
    id = 'edit_comment'
    title = _('edit comment')

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

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

from cubicweb.web.views.basecontrollers import JSonController

def js_add_comment(self, commented, text, format):
    rql = 'INSERT Comment C: C content %(text)s, C content_format %(format)s, C comments X WHERE X eid %(x)s'
    rset = self.req.execute(rql, {'format' : format, 'text' : text, 'x' : commented}, 'x')
    return u''

JSonController.js_add_comment = js_add_comment

def js_edit_comment(self, comment, text, format):
    rql = 'SET C content %(text)s, C content_format %(format)s WHERE C eid %(x)s'
    rset = self.req.execute(rql, {'format' : format, 'text' : text, 'x' : comment}, 'x')
    return u''

JSonController.js_edit_comment = js_edit_comment