Newer
Older
"""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"
from logilab.mtconverter import xml_escape
from cubicweb.selectors import (one_line_rset, but_etype, implements,
match_kwargs, score_entity)
from cubicweb.common.uilib import rql_for_eid, cut, safe_cut
from cubicweb.web.action import LinkToEntityAction, Action
from cubicweb.web.form import FormViewMixIn
from cubicweb.web.views import primary, baseviews, xmlrss
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')
# XXX this is probably *very* inefficient since we'll fetch all entities created by the user
uicfg.primaryview_section.tag_object_of(('*', 'created_by', 'CWUser'), 'relations')
uicfg.primaryview_display_ctrl.tag_object_of(
{'vid': 'list', 'label': _('latest comment(s):'), 'limit': True,
'filter': lambda rset: rset.filtered_rset(lambda x: x.e_schema == 'Comment')})
if 'registration' in req.vreg.config.cubes():
link = u'<a href="%s">%s</a> or ' % (req.build_url('register'),
req._(u'register'))
else:
link = u''
link += u'<a href="%s">%s</a>' % (req.build_url('login'),
req._(u'login'))
return link
# comment views ###############################################################
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 CommentOneLineView(baseviews.OneLineView):
def cell_call(self, row, col, **kwargs):
entity = self.entity(row, col)
root = entity.root()
self.w(u'[<a href="%s">#%s</a>] '
% (xml_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 = xml_escape(cut(content, maxsize))
xml_escape(entity.absolute_url()), entity.eid, content))
class CommentTreeItemView(baseviews.ListItemView):
id = 'treeitem'
self.req.add_js('cubicweb.ajax.js')
self.req.add_css('cubes.comment.css')
self.w(u'<div class="commentInfo">')
self.w(self.format_date(entity.creation_date))
self.w(u' %s' % self.format_time(entity.creation_date))
if entity.creator:
authorlink = entity.creator.view('oneline')
self.w(u', %s <span class="author">%s</span> \n'
% (self.req._('written by'), authorlink,))
replyaction = actions.select_object('reply_comment', self.req,
rset=self.rset, row=row)
if replyaction is not None:
url = self.req.build_ajax_replace_url(
'comment%sHolder' % entity.eid, rql_for_eid(entity.eid),
'inlinecomment')
self.w(u' | <span class="replyto">%s <a href="%s">%s</a></span>'
% (_login_register_link(self.req),
else:
self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
editaction = actions.select_object('edit_comment', self.req,
rset=self.rset, row=row)
if editaction is not None:
url = self.req.build_ajax_replace_url(
'comment%s' % entity.eid, rql_for_eid(entity.eid),
'editcomment')
self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
Sandrine Ribeau
committed
deleteaction = actions.select_object('delete_comment', self.req,
rset=self.rset, row=row)
if deleteaction is not None:
url = self.req.build_ajax_replace_url(
'comment%s' % entity.eid, rql_for_eid(entity.eid),
'deleteconf')
self.w(u' | <span class="replyto"><a href="%s">%s</a></span>'
% (xml_escape(url), self.req._(deleteaction.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="commentBody">%s</div>\n' % text)
self.w(u'<div id="comment%sHolder"></div>' % entity.eid)
class CommentThreadView(TreeViewMixIn, baseviews.ListView):
"""a recursive tree 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'
def cell_call(self, row, col):
entity = self.entity(row, col)
self.wview('inlinecommentform', None, commented=entity)
Graziella Toutoungis
committed
class InlineEditCommentForm(FormViewMixIn, EntityView):
id = 'editcomment'
__select__ = implements('Comment')
Graziella Toutoungis
committed
jsfunc = "processComment(%s, '%s')"
jsonmeth = 'edit_comment'
Graziella Toutoungis
committed
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:
# 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, self.jsonmeth)),
Button(stdmsgs.BUTTON_CANCEL,
onclick=self.jsfunc % (jseid, ''))]
form = self.vreg['forms'].select('edition', self.req,
entity=newcomment,
form_buttons=buttons)
self.w(u'<div id="comment%sSlot">%s</div>' % (
commented.eid, form.form_render(main_form_title=u'',
display_label=False,
Graziella Toutoungis
committed
class InlineCommentForm(InlineEditCommentForm):
id = 'inlinecommentform'
__select__ = match_kwargs('commented') # explicit call when it makes sense
jsonmeth = 'add_comment'
Graziella Toutoungis
committed
def call(self, commented):
self.initialize_varmaker()
newcomment = self.vreg['etypes'].etype_class('Comment')(self.req)
Graziella Toutoungis
committed
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'))
req.add_js( ('cubicweb.ajax.js', 'cubes.comment.js') )
self.w(u'<div id="%s" class="%s" cubicweb:rooteid="%s">' % (
self.div_id(), self.div_class(), eid))
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')
self.w(u'<h4>%s</h4>' % (req._('Comment_plural')))
addcomment = self.vreg['actions'].select_object('reply_comment', req,
rset=self.rset,
row=row, col=col)
if addcomment is not None:
url = req.build_ajax_replace_url(
'comment%sHolder' % eid, rql_for_eid(eid), 'inlinecomment')
self.w(u' (<a href="%s">%s</a>)' % (url, req._(addcomment.title)))
# XXX still necessary?
#if req.use_fckeditor() and req.property_value('ui.default-text-format') == 'text/html':
# req.fckeditor_config()
if req.cnx.anonymous_connection:
self.w(u'%s %s' % (_login_register_link(req), req._(u'to comment')))
self.w(u'<div id="comment%sHolder"></div>' % eid)
if rset.rowcount:
self.w(u'<ul class="comment">')
for i in xrange(rset.rowcount):
self.wview('tree', rset, row=i, full=True)
self.w(u'</ul>')
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'
comment = self.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"""
__select__ = LinkToEntityAction.__select__ & but_etype('Comment')
title = _('add comment')
category = 'hidden'
class EditCommentAction(Action):
id = 'edit_comment'
__select__ = one_line_rset() & implements('Comment') & has_permission('update')
category = 'hidden'
order = 110
def url(self):
return self.build_url(rql=self.rset.printable_rql(), vid='edition')
Sandrine Ribeau
committed
id = 'delete_comment'
__select__ = implements('Comment') & \
score_entity(lambda x: not x.reverse_comments and x.has_perm('delete'))
Sandrine Ribeau
committed
title = _('delete comment')
category = 'hidden'
order = 110
def url(self):
return self.build_url(rql=self.rset.printable_rql(), vid='deleteconf')
# add some comments related methods to the 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')
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')
Sandrine Ribeau
committed
# RSS view ####################################################################
class RssItemCommentView(xmlrss.RSSItemView):
__select__ = implements('Comment')
def cell_call(self, row, col):
entity = self.complete_entity(row, col)
self.w(u'<item>\n')
self.w(u'<guid isPermaLink="true">%s</guid>\n'
% xml_escape(entity.absolute_url()))
self.render_title_link(entity)
description = entity.dc_description(format='text/html') + \
self.req._(u'about') + \
u' <a href=%s>%s</a>' % (entity.root().absolute_url(),
entity.root().dc_title())
Sandrine Ribeau
committed
self._marker('description', description)
self._marker('dc:date', entity.dc_date(self.date_format))
self.render_entity_creator(entity)
self.w(u'</item>\n')
self.wview('rssitem', entity.related('comments', 'object'), 'null')