merge r46437 and r46946 from davisagli-features: respect the per-comment mime_type setting, and use the old cooked text/html from legacy comments when migrating
svn path=/plone.app.discussion/trunk/; revision=48356
This commit is contained in:
parent
bf5946367a
commit
eb004aab44
13
CHANGES.txt
13
CHANGES.txt
@ -4,6 +4,19 @@ Changelog
|
|||||||
2.0b1 (Unreleased)
|
2.0b1 (Unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Use the cooked text of legacy comments when migrating.
|
||||||
|
[davisagli]
|
||||||
|
|
||||||
|
- Make sure that comment text is transformed to plain text when indexing.
|
||||||
|
[davisagli]
|
||||||
|
|
||||||
|
- Move logic for transforming comment text to the Comment class's getText
|
||||||
|
method. Use a comment instance's mime_type attribute in preference to the
|
||||||
|
global setting for the source mimetype. Use text/x-html-safe as the target
|
||||||
|
mimetype to make sure the safe HTML filter is applied, in case the source is
|
||||||
|
untrusted HTML.
|
||||||
|
[davisagli]
|
||||||
|
|
||||||
- Provide a filter_callback option to the migration view, so that a custom
|
- Provide a filter_callback option to the migration view, so that a custom
|
||||||
policy for which comments get migrated can be implemented.
|
policy for which comments get migrated can be implemented.
|
||||||
[davisagli]
|
[davisagli]
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<div class="commentBody">
|
<div class="commentBody">
|
||||||
|
|
||||||
<span tal:replace="structure python:view.cook(reply.getText())" />
|
<span tal:replace="structure reply/getText" />
|
||||||
|
|
||||||
<div class="commentActions">
|
<div class="commentActions">
|
||||||
<form name="delete"
|
<form name="delete"
|
||||||
|
@ -271,17 +271,6 @@ class CommentsViewlet(ViewletBase):
|
|||||||
|
|
||||||
# view methods
|
# view methods
|
||||||
|
|
||||||
def cook(self, text):
|
|
||||||
transforms = getToolByName(self, 'portal_transforms')
|
|
||||||
targetMimetype = 'text/html'
|
|
||||||
registry = queryUtility(IRegistry)
|
|
||||||
settings = registry.forInterface(IDiscussionSettings, check=False)
|
|
||||||
mimetype = settings.text_transform
|
|
||||||
return transforms.convertTo(targetMimetype,
|
|
||||||
text,
|
|
||||||
context=self,
|
|
||||||
mimetype=mimetype).getData()
|
|
||||||
|
|
||||||
def can_reply(self):
|
def can_reply(self):
|
||||||
"""Returns true if current user has the 'Reply to item' permission.
|
"""Returns true if current user has the 'Reply to item' permission.
|
||||||
"""
|
"""
|
||||||
|
@ -70,7 +70,8 @@ class View(BrowserView):
|
|||||||
# create a reply object
|
# create a reply object
|
||||||
comment = CommentFactory()
|
comment = CommentFactory()
|
||||||
comment.title = reply.Title()
|
comment.title = reply.Title()
|
||||||
comment.text = reply.text
|
comment.text = reply.cooked_text
|
||||||
|
comment.mime_type = 'text/html'
|
||||||
comment.creator = reply.Creator()
|
comment.creator = reply.Creator()
|
||||||
|
|
||||||
email = reply.getProperty('email', None)
|
email = reply.getProperty('email', None)
|
||||||
|
@ -110,7 +110,7 @@
|
|||||||
tal:content="item/in_response_to" />
|
tal:content="item/in_response_to" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span tal:replace="structure python:view.cook(item.Description)" />
|
<span tal:replace="structure item/Description" />
|
||||||
<a href=""
|
<a href=""
|
||||||
tal:attributes="href string:${item/getURL}/getText"
|
tal:attributes="href string:${item/getURL}/getText"
|
||||||
tal:condition="python:item.Description.endswith('[...]')"
|
tal:condition="python:item.Description.endswith('[...]')"
|
||||||
|
@ -2,7 +2,6 @@ from Acquisition import aq_inner, aq_parent
|
|||||||
|
|
||||||
from Products.Five.browser import BrowserView
|
from Products.Five.browser import BrowserView
|
||||||
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
|
||||||
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
|
|
||||||
|
|
||||||
from Products.CMFCore.utils import getToolByName
|
from Products.CMFCore.utils import getToolByName
|
||||||
|
|
||||||
@ -37,9 +36,6 @@ class View(BrowserView):
|
|||||||
sort_order='reverse')
|
sort_order='reverse')
|
||||||
return self.template()
|
return self.template()
|
||||||
|
|
||||||
def cook(self, text):
|
|
||||||
return text
|
|
||||||
|
|
||||||
def moderation_enabled(self):
|
def moderation_enabled(self):
|
||||||
"""Returns true if a 'review workflow' is enabled on 'Discussion Item'
|
"""Returns true if a 'review workflow' is enabled on 'Discussion Item'
|
||||||
content type. A 'review workflow' is characterized by implementing
|
content type. A 'review workflow' is characterized by implementing
|
||||||
|
@ -77,14 +77,14 @@ def creator(object):
|
|||||||
@indexer(IComment)
|
@indexer(IComment)
|
||||||
def description(object):
|
def description(object):
|
||||||
# Return the first 25 words of the comment text and append ' [...]'
|
# Return the first 25 words of the comment text and append ' [...]'
|
||||||
text = join(object.text.split()[:MAX_DESCRIPTION])
|
text = join(object.getText(targetMimetype='text/plain').split()[:MAX_DESCRIPTION])
|
||||||
if len(object.text.split()) > 25:
|
if len(object.getText().split()) > 25:
|
||||||
text += " [...]"
|
text += " [...]"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
@indexer(IComment)
|
@indexer(IComment)
|
||||||
def searchable_text(object):
|
def searchable_text(object):
|
||||||
return object.text
|
return object.getText(targetMimetype='text/plain')
|
||||||
|
|
||||||
@indexer(IComment)
|
@indexer(IComment)
|
||||||
def in_response_to(object):
|
def in_response_to(object):
|
||||||
|
@ -79,7 +79,7 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
|
|
||||||
title = u""
|
title = u""
|
||||||
|
|
||||||
mime_type = "text/plain"
|
mime_type = None
|
||||||
text = u""
|
text = u""
|
||||||
|
|
||||||
creator = None
|
creator = None
|
||||||
@ -113,10 +113,26 @@ class Comment(CatalogAware, WorkflowAware, DynamicType, Traversable,
|
|||||||
"""
|
"""
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def getText(self):
|
def getText(self, targetMimetype=None):
|
||||||
"""The body text of a comment.
|
"""The body text of a comment.
|
||||||
"""
|
"""
|
||||||
return self.text
|
transforms = getToolByName(self, 'portal_transforms')
|
||||||
|
|
||||||
|
if targetMimetype is None:
|
||||||
|
targetMimetype = 'text/x-html-safe'
|
||||||
|
|
||||||
|
sourceMimetype = getattr(self, 'mime_type', None)
|
||||||
|
if sourceMimetype is None:
|
||||||
|
registry = queryUtility(IRegistry)
|
||||||
|
settings = registry.forInterface(IDiscussionSettings, check=False)
|
||||||
|
sourceMimetype = settings.text_transform
|
||||||
|
text = self.text
|
||||||
|
if isinstance(text, unicode):
|
||||||
|
text = text.encode('utf8')
|
||||||
|
return transforms.convertTo(targetMimetype,
|
||||||
|
text,
|
||||||
|
context=self,
|
||||||
|
mimetype=sourceMimetype).getData()
|
||||||
|
|
||||||
def Title(self):
|
def Title(self):
|
||||||
"""The title of the comment.
|
"""The title of the comment.
|
||||||
|
@ -123,6 +123,44 @@ class CommentTest(PloneTestCase):
|
|||||||
comment1 = createObject('plone.Comment')
|
comment1 = createObject('plone.Comment')
|
||||||
self.assertEquals(comment1.Type(), 'Comment')
|
self.assertEquals(comment1.Type(), 'Comment')
|
||||||
|
|
||||||
|
def test_getText(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = """First paragraph
|
||||||
|
|
||||||
|
Second paragraph"""
|
||||||
|
self.assertEquals(comment1.getText(),
|
||||||
|
"<p>First paragraph<br /><br /> Second paragraph</p>")
|
||||||
|
|
||||||
|
def test_getText_escapes_HTML(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = """<b>Got HTML?</b>"""
|
||||||
|
self.assertEquals(comment1.getText(),
|
||||||
|
"<p><b>Got HTML?</b></p>")
|
||||||
|
|
||||||
|
def test_getText_with_non_ascii_characters(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = u"""Umlaute sind ä, ö und ü."""
|
||||||
|
self.assertEquals(comment1.getText(),
|
||||||
|
'<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>')
|
||||||
|
|
||||||
|
def test_getText_doesnt_link(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = "Go to http://www.plone.org"
|
||||||
|
self.assertEquals(comment1.getText(),
|
||||||
|
"<p>Go to http://www.plone.org</p>")
|
||||||
|
|
||||||
|
def test_getText_uses_comment_mime_type(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = "Go to http://www.plone.org"
|
||||||
|
comment1.mime_type = 'text/x-web-intelligent'
|
||||||
|
self.assertEquals(comment1.getText(),
|
||||||
|
'Go to <a href="http://www.plone.org" rel="nofollow">http://www.plone.org</a>')
|
||||||
|
|
||||||
|
def test_getText_w_custom_targetMimetype(self):
|
||||||
|
comment1 = createObject('plone.Comment')
|
||||||
|
comment1.text = 'para'
|
||||||
|
self.assertEquals(comment1.getText(targetMimetype='text/plain'), 'para')
|
||||||
|
|
||||||
def test_traversal(self):
|
def test_traversal(self):
|
||||||
# make sure comments are traversable, have an id, absolute_url and
|
# make sure comments are traversable, have an id, absolute_url and
|
||||||
# physical path
|
# physical path
|
||||||
|
@ -228,28 +228,6 @@ class TestCommentsViewlet(PloneTestCase):
|
|||||||
settings = registry.forInterface(IDiscussionSettings)
|
settings = registry.forInterface(IDiscussionSettings)
|
||||||
settings.globally_enabled = True
|
settings.globally_enabled = True
|
||||||
|
|
||||||
def test_cook(self):
|
|
||||||
text = """First paragraph
|
|
||||||
|
|
||||||
Second paragraph"""
|
|
||||||
self.assertEquals(self.viewlet.cook(text),
|
|
||||||
"<p>First paragraph<br /><br /> Second paragraph</p>")
|
|
||||||
|
|
||||||
def test_cook_no_html(self):
|
|
||||||
text = """<b>Got HTML?</b>"""
|
|
||||||
self.assertEquals(self.viewlet.cook(text),
|
|
||||||
"<p><b>Got HTML?</b></p>")
|
|
||||||
|
|
||||||
def test_cook_with_no_ascii_characters(self):
|
|
||||||
text = """Umlaute sind ä, ö und ü."""
|
|
||||||
self.assertEquals(self.viewlet.cook(text),
|
|
||||||
"<p>Umlaute sind \xc3\xa4, \xc3\xb6 und \xc3\xbc.</p>")
|
|
||||||
|
|
||||||
def test_cook_links(self):
|
|
||||||
text = "Go to http://www.plone.org"
|
|
||||||
self.assertEquals(self.viewlet.cook(text),
|
|
||||||
"<p>Go to http://www.plone.org</p>")
|
|
||||||
|
|
||||||
def test_can_reply(self):
|
def test_can_reply(self):
|
||||||
# Portal owner can reply
|
# Portal owner can reply
|
||||||
self.failUnless(self.viewlet.can_reply())
|
self.failUnless(self.viewlet.can_reply())
|
||||||
|
@ -69,7 +69,8 @@ class MigrationTest(PloneTestCase):
|
|||||||
comment1 = conversation.values()[0]
|
comment1 = conversation.values()[0]
|
||||||
self.assert_(IComment.providedBy(comment1))
|
self.assert_(IComment.providedBy(comment1))
|
||||||
self.assertEquals(comment1.Title(), 'Jim on Document 1')
|
self.assertEquals(comment1.Title(), 'Jim on Document 1')
|
||||||
self.assertEquals(comment1.text, 'My Text')
|
self.assertEquals(comment1.text, '<p>My Text</p>\n')
|
||||||
|
self.assertEquals(comment1.mime_type, 'text/html')
|
||||||
self.assertEquals(comment1.Creator(), 'Jim')
|
self.assertEquals(comment1.Creator(), 'Jim')
|
||||||
self.assertEquals(comment1.creation_date,
|
self.assertEquals(comment1.creation_date,
|
||||||
datetime(2003, 3, 11, 9, 28, 6))
|
datetime(2003, 3, 11, 9, 28, 6))
|
||||||
|
Loading…
Reference in New Issue
Block a user