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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user