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 itertools import count
from logilab.mtconverter import html_escape
from cubicweb.selectors import (one_line_rset, but_etype, implements,
has_permission, relation_possible, yes)
from cubicweb.view import EntityView
from cubicweb.selectors import match_kwargs, accept
from cubicweb.common.uilib import rql_for_eid, cut, safe_cut, ajax_replace_url
from cubicweb.common.mixins import TreeViewMixIn
from cubicweb.web import stdmsgs
from cubicweb.web.action import (LinkToEntityAction, Action)
from cubicweb.web.views import baseviews, baseforms
from cubicweb.web.component import EntityVComponent
from cubicweb.web.views.basecontrollers import JSonController
_ = unicode
# comment views ###############################################################
class CommentPrimaryView(baseviews.PrimaryView):
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 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):
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'
def cell_call(self, row, col, **kwargs):
_ = self.req._
self.req.add_js('cubicweb.ajax.js')
self.req.add_css('cubes.comment.css')
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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"""
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'
def cell_call(self, row, col):
entity = self.entity(row, col)
self.wview('inlinecommentform', None, commented=entity)
class InlineCommentForm(baseforms.CreationForm):
id = 'inlinecommentform'
__select__ = match_kwargs('commented') # explicit call when it makes sense
title = None # hidden
EDITION_BODY = u"""
<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>
</div>
</div>
"""
def call(self, commented):
self.req.add_js('cubicweb.ajax.js','cubes.comment.js')
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,
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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') )
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# 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 ###########################################################
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')
def cell_call(self, row, col, view=None, orderby='diem'):
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))
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':
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')
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>')
self.w(u'<div id="comment%sHolder"></div>' % eid)
self.w(u'</div>')
baseviews.PRIMARY_SKIP_RELS.add('comments') # displayed by the above component
# comment actions #############################################################
class ReplyCommentAction(LinkToEntityAction):
id = 'reply_comment'
__select__ = LinkToEntityAction.__select__ & implements('Comments')
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"""
__select__ = LinkToEntityAction.__select__ & but_etype('Comment')
etype = 'Comment'
rtype = 'comments'
target = 'subject'
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')
# add some comments related methods to the 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''
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''