[silva.core.editor][Emiliano D'Alterio] Added new function to ch...
[email protected] Tue, 17 Sep 2013 11:21:38 +0200
| Newsgroups | gmane.comp.web.zope.silva.cvs |
|---|---|
| Message-ID | <[email protected]> |
author: Emiliano D'Alterio
date: Tue Sep 17 11:21:13 2013 +0200
revision: 265:58e796014b19 in silva.core.editor
branch:
details: https://hg.infrae.com/silva.core.editor?cmd=changeset;node=58e796014b19
modified: src/silva/core/editor/tests/test_transform_output_image.py src/silva/core/editor/transform/editor/output.py
added: src/silva/core/editor/tests/test_transform_output_image.py
removed:
log: Added new function to check and fix invalid image block structures
created in the editor. Added 7 new tests to test the new function.
diffstat:
src/silva/core/editor/tests/test_transform_output_image.py | 316 +++++++++++++
src/silva/core/editor/transform/editor/output.py | 66 ++
2 files changed, 382 insertions(+), 0 deletions(-)
diffs (411 lines):
diff -r 3e0394d678a8 -r 58e796014b19 src/silva/core/editor/tests/test_transform_output_image.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/editor/tests/test_transform_output_image.py Tue Sep 17 11:21:13 2013 +0200
@@ -0,0 +1,316 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2011-2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import unittest
+
+from Products.Silva.testing import TestCase
+from Products.Silva.testing import TestRequest
+
+from zope.component import getMultiAdapter
+
+
+from ..testing import FunctionalLayer
+from ..text import Text
+from ..transform.interfaces import ISaveEditorFilter
+from ..transform.interfaces import ITransformerFactory
+
+
+class OutputTransformTestCase(TestCase):
+ layer = FunctionalLayer
+
+ def setUp(self):
+ self.root = self.layer.get_application()
+ self.layer.login('author')
+
+ factory = self.root.manage_addProduct['Silva']
+ factory.manage_addMockupVersionedContent('document', 'Document')
+ factory.manage_addMockupVersionedContent('target', 'Document Target')
+
+ version = self.root.document.get_editable()
+ version.test = Text('test')
+
+ def transform(self, text, filter):
+ """Helper to call transform.
+ """
+ version = self.root.document.get_editable()
+ request = TestRequest()
+ factory = getMultiAdapter((version, request), ITransformerFactory)
+ transformer = factory('test', version.test, text, filter)
+ return unicode(transformer)
+
+ def test_link_in_caption(self):
+ """ There should be no link wrapped around caption text.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ <span class="image-caption">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">THIS CAPTION TEXT IS WRAPPED IN A LINK, IT SHOULDN'T.
+ </a>
+ </span>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ <span class="image-caption">THIS CAPTION TEXT IS WRAPPED IN A LINK, IT SHOULDN'T.</span>
+ </div>
+ """
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_double_image_wrapper(self):
+ """ The div image block (@class="image")
+ shouldn't be nested in a div with the same class.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <div class="image ">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_multiple_links(self):
+ """ Only one link is allowed inside an image block.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ </a>
+ </a>
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <span class="image-caption">CAPTION</span>
+ </a>
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ </a>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_multiple_images(self):
+ """ Only one image is allowed inside an image block.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ <span class="image-caption">CAPTION</span>
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_nested_paragraphs(self):
+ """ Only one <a>, one <img> and one <span> are allowed inside an image block.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <p>This P is nested inside the image block structure, it shouldn't be here.</p>
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ <p>This P is nested inside the image block structure, it shouldn't be here.</p>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_multiple_wrappers(self):
+ """ There should be only one div image block wrapper.
+ """
+ editor_image_block = """
+ <div class="image ">
+ <div class="image "><div class="image "></div>
+ <div class="image "><div class="image "><div class="image "></div></div>
+ <a class="image-link"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ <div class="image "><img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" /><div class="image "></div>
+ </a>
+ </div>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ </div>
+ """
+
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+ def test_wrong_classes(self):
+ """ Link inside image block should have 'image-link' class
+ while caption span should have 'image-caption' class
+ """
+ editor_image_block = """
+ <div class="image ">
+ <a class="wrong-class"
+ data-silva-anchor="Infrae"
+ data-silva-url="http://infrae.com"
+ target="_self"
+ title="Silva 3.0 Documentation">
+ <img alt="IMAGE ALT TEXT" data-silva-url="http://infrae.com/contact/infrae_qr_vcard.png" />
+ </a>
+ <span class="wrong-class">CAPTION</span>
+ </div>
+ """
+
+ actual_image_block = self.transform(editor_image_block,
+ ISaveEditorFilter)
+
+ expected_image_block = """
+ <div class="image ">
+ <a class="image-link"
+ target="_self"
+ title="Silva 3.0 Documentation"
+ href="http://infrae.com"
+ anchor="Infrae">
+ <img alt="IMAGE ALT TEXT" src="http://infrae.com/contact/infrae_qr_vcard.png">
+ </a>
+ <span class="image-caption">CAPTION</span>
+ </div>
+ """
+
+ self.assertXMLEqual(expected_image_block, actual_image_block)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(OutputTransformTestCase))
+ return suite
diff -r 3e0394d678a8 -r 58e796014b19 src/silva/core/editor/transform/editor/output.py
--- a/src/silva/core/editor/transform/editor/output.py Mon Sep 02 14:25:22 2013 +0200
+++ b/src/silva/core/editor/transform/editor/output.py Tue Sep 17 11:21:13 2013 +0200
@@ -3,6 +3,7 @@
# See also LICENSE.txt
import urlparse
+from lxml import etree
from five import grok
from zope.component import queryUtility
@@ -53,6 +54,60 @@
del tag.attrib[name]
+def clean_image_block(block_tree):
+ """ Cleans up an image block to assure that
+ it has the correct structure.
+ """
+ image = None
+ img_wrapper = None
+ caption = None
+ image_found = False
+ caption_found = False
+
+ ## We get all the block descendants using lxml (should be "depth-first")
+ ## in order to get image and caption elements, if any.
+ for des in block_tree.iterdescendants():
+ ## We only take the first img element found.
+ if des.tag == 'img' and not image_found:
+ image_found = True
+ ## We set the image element.
+ image = des
+ ## If the img element is wrapped by a link
+ ## we set the image_wrapper too.
+ if des.getparent().tag == 'a':
+ img_wrapper = des.getparent()
+ ## If the class has been modified we put the correct one.
+ img_wrapper.attrib['class'] = 'image-link'
+
+ ## We only take the first span element (caption) found.
+ if des.tag == 'span' and not caption_found:
+ caption_found = True
+ ## We set the caption element.
+ caption = des
+ ## If the class has been modified we put the correct one.
+ caption.attrib['class'] = 'image-caption'
+
+ ## Sanitazing the caption, we strip out every element inside the span
+ ## preserving the content and thus all the texts present.
+ if caption is not None:
+ etree.strip_tags(caption, '*')
+
+ ## We go through the descendants again to mark invalid elements.
+ for des in block_tree.iterdescendants():
+ ## Invalid elements are all those elements which are neither the image
+ ## nor the caption, nor the image_wrapper.
+ if des is image or des is img_wrapper or des is caption:
+ continue
+ ## We remove invalid tags texts.
+ des.text = ''
+ ## We mark invalid tags for removal.
+ des.tag = 'tag_to_be_stripped_out'
+
+ ## We finally strip out tags marked as invalid
+ ## now the image block should have the correct structure.
+ etree.strip_tags(block_tree, 'tag_to_be_stripped_out')
+
+
class SilvaReferenceTransformationFilter(ReferenceTransformationFilter):
"""Base class to update a reference information out of data-silva-
tags.
@@ -120,7 +175,18 @@
def __call__(self, tree):
for block in tree.xpath('//div[contains(@class, "image")]'):
+ ## we clean this block up
+ clean_image_block(block)
+
+ ## In some cases there can be invalid nested blocks
+ ## we can skip these blocks since the cleaner routine
+ ## has already taken care of them.
+ ## So the reference we have is pointing to
+ ## an already stripped out block.
images = block.xpath('descendant::img')
+ if len(images) < 1:
+ continue
+
assert len(images) == 1, u"Invalid image construction"
image = images[0]
if 'src' in image.attrib: