[Erp5-report] r31339 fabien - in /erp5/trunk/products: ERP5Form/ ERP5OOo/ ERP5OOo/tests/ ER...

nobody at svn.erp5.org nobody at svn.erp5.org
Wed Dec 16 16:23:18 CET 2009


Author: fabien
Date: Wed Dec 16 16:23:14 2009
New Revision: 31339

URL: http://svn.erp5.org?rev=31339&view=rev
Log:
Enable image mapping with odg documents :

FormPrintout : clean a bit code
FormPrintout._replaceXmlByForm : change the xpath expression to get the node
from one level higher. In this way we can have all the node tree used for the
field. Add style in style dict for this node
FormPrintout._createOdfUniqueFileName : use quote_plus in both cases, add a comment to explain it
ImageField : add a method _replaceImage used to replace an image in an odg file
FormPrintoutAsODG : add test for proxyfields
Formulator.Field, Formulator.Widget : change render_odg signature to accept
more parameters required for image mapping. Add First level node.
ERP5OOo/tests/test_document/Foo_001.odg add some descriptions arround fields, to make easier to understand the purpose
of each element in the document.

reviewed and approved by Romain

Modified:
    erp5/trunk/products/ERP5Form/ImageField.py
    erp5/trunk/products/ERP5OOo/FormPrintout.py
    erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODG.py
    erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODT.py
    erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odg
    erp5/trunk/products/Formulator/Field.py
    erp5/trunk/products/Formulator/Widget.py

Modified: erp5/trunk/products/ERP5Form/ImageField.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Form/ImageField.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Form/ImageField.py [utf8] (original)
+++ erp5/trunk/products/ERP5Form/ImageField.py [utf8] Wed Dec 16 16:23:14 2009
@@ -29,7 +29,17 @@
 from Products.Formulator import Widget, Validator
 from Products.Formulator.Field import ZMIField
 from Products.Formulator.DummyField import fields
+from lxml.etree import Element
+from Acquisition import aq_base
+from lxml import etree
 
+DRAW_URI = 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'
+TEXT_URI = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
+
+NSMAP = {
+          'draw': DRAW_URI,
+          'text': TEXT_URI,
+        }
 
 class ImageFieldWidget(Widget.TextWidget):
     """ImageField widget.
@@ -92,6 +102,79 @@
             extra=extra,
         )
 
+    def _replaceImage(self, image_frame_node, ooo_builder, image_field,
+        printout, REQUEST, attr_dict):
+      """
+      Replace the image in an odg file using an ERP5Form image field.
+      return True if the image have been found, else return False
+      """
+      image_node = image_frame_node.getchildren()[0]
+      path = '/'.join(REQUEST.physicalPathFromURL(image_field.get_value('default')))
+      if path in (None, ''):
+        # not possible to add image, remove the existing one
+        image_frame_node = image_node.getparent()
+        image_frame_node.remove(image_node)
+        return False
+      path = path.encode()
+      image_object = image_field.getPortalObject().restrictedTraverse(path)
+      image_parameter_dict = {
+                    'display': image_field.get_value('image_display'),
+                    'format': image_field.get_value('image_format')
+                   }
+      picture_type, image_data = image_object.convert(**image_parameter_dict)
+      if image_data is None:
+        # not possible to add image, remove the existing one
+        image_frame_node = image_node.getparent()
+        image_frame_node.remove(image_node)
+        return False
+      picture_path = printout._createOdfUniqueFileName(path=path,
+          picture_type=picture_type)
+      ooo_builder.addFileEntry(picture_path, media_type=picture_type, content=image_data)
+      width, height = printout._getPictureSize(image_object, image_node)
+      image_node.set('{%s}href' % image_node.nsmap['xlink'], picture_path)
+      image_frame_node.set('{%s}width' % image_node.nsmap['svg'], width)
+      image_frame_node.set('{%s}height' % image_node.nsmap['svg'], height)
+      attr_dict.setdefault(image_node.tag, {}).update(image_node.attrib)
+      attr_dict.setdefault(image_frame_node.tag, {}).update(image_frame_node.attrib)
+      return True
+
+    def render_odg(self, field, as_string, local_name, target_node, printout,
+        REQUEST, ooo_builder, attr_dict=None):
+      """
+        return an image xml node rendered in odg format
+        if as_string is True (default) the returned value is a string (xml
+        reprensation of the node), if it's False, the value returned is the node
+        object.
+        attr_dict can be used for additional parameters (like style).
+      """
+      # replace the image in the odg document
+      if not self._replaceImage(target_node, ooo_builder, field, printout,
+          REQUEST, attr_dict):
+        # if image is not found, return None
+        return None
+
+      if attr_dict is None:
+        attr_dict = {}
+
+      draw_frame_tag_name = '{%s}%s' % (DRAW_URI, 'frame')
+      draw_frame_node = Element(draw_frame_tag_name, nsmap=NSMAP)
+      draw_frame_node.attrib.update(attr_dict.get(draw_frame_tag_name, {}))
+
+      draw_image_tag_name = '{%s}%s' % (DRAW_URI, 'image')
+      draw_image_node = Element(draw_image_tag_name, nsmap=NSMAP)
+      draw_image_node.attrib.update(attr_dict.get(draw_image_tag_name, {}))
+
+      text_p_tag_name = '{%s}%s' % (TEXT_URI, local_name)
+      text_p_node = Element(text_p_tag_name, nsmap=NSMAP)
+      text_p_node.attrib.update(attr_dict.get(text_p_tag_name, {}))
+
+      draw_image_node.append(text_p_node)
+      draw_frame_node.append(draw_image_node)
+
+      if as_string:
+        return etree.tostring(draw_frame_node)
+      return draw_frame_node
+
 ImageFieldWidgetInstance = ImageFieldWidget()
 ImageFieldValidatorInstance = Validator.StringValidator()
 

Modified: erp5/trunk/products/ERP5OOo/FormPrintout.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/FormPrintout.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/ERP5OOo/FormPrintout.py [utf8] (original)
+++ erp5/trunk/products/ERP5OOo/FormPrintout.py [utf8] Wed Dec 16 16:23:14 2009
@@ -43,7 +43,6 @@
 from urllib import quote, quote_plus
 from copy import deepcopy
 from lxml import etree
-from lxml.etree import _Element, _ElementStringResult
 from zLOG import LOG, DEBUG, INFO, WARNING
 from mimetypes import guess_extension
 from DateTime import DateTime
@@ -270,7 +269,8 @@
     if here is None:
       raise ValueError, 'Can not create a ODF Document without a parent acquisition context'
     form = extra_context['form']
-    if not extra_context.has_key('printout_template') or extra_context['printout_template'] is None:
+    if not extra_context.has_key('printout_template') or \
+        extra_context['printout_template'] is None:
       raise ValueError, 'Can not create a ODF Document without a printout template'
 
     odf_template = extra_context['printout_template']
@@ -281,7 +281,7 @@
       ooo_document = odf_template.pt_render(here, extra_context=extra_context)
     else:
       # File object can be a template
-      ooo_document = odf_template 
+      ooo_document = odf_template
 
     # Create a new builder instance
     ooo_builder = OOoBuilder(ooo_document)
@@ -393,7 +393,7 @@
       form = getattr(here, form_id)
 
       target_element_tree = deepcopy(temporary_element_tree)
-      # remove original target in the ODF template 
+      # remove original target in the ODF template
       if index == 0:
         office_body.remove(original_target)
       else:
@@ -514,21 +514,23 @@
     picture_type = picture.getContentType()
     picture_path = self._createOdfUniqueFileName(path=path, picture_type=picture_type)
     ooo_builder.addFileEntry(picture_path, media_type=picture_type, content=picture_data)
-    picture_size = self._getPictureSize(picture, image_node)
+    width, height = self._getPictureSize(picture, image_node)
     image_node.set('{%s}href' % element_tree.nsmap['xlink'], picture_path)
-    image_frame.set('{%s}width' % element_tree.nsmap['svg'], picture_size[0])
-    image_frame.set('{%s}height' % element_tree.nsmap['svg'], picture_size[1])
+    image_frame.set('{%s}width' % element_tree.nsmap['svg'], width)
+    image_frame.set('{%s}height' % element_tree.nsmap['svg'], height)
     # set when using report section
     self._setUniqueElementName(image_field.id, iteration_index, image_xpath, element_tree)
 
   def _createOdfUniqueFileName(self, path='', picture_type=''):
     extension = guess_extension(picture_type)
-    picture_path = 'Pictures/%s%s' % (quote_plus(path), extension)     
+    # here, it's needed to use quote_plus to escape '/' caracters to make valid
+    # paths in the odf archive.
+    picture_path = 'Pictures/%s%s' % (quote_plus(path), extension)
     if picture_path not in self.odf_existent_name_list:
       return picture_path
     number = 0
     while True:
-      picture_path = 'Pictures/%s_%s%s' % (path, number, extension)
+      picture_path = 'Pictures/%s_%s%s' % (quote_plus(path), number, extension)
       if picture_path not in self.odf_existent_name_list:
         return picture_path
       number += 1
@@ -949,15 +951,22 @@
 
   def _replaceXmlByForm(self, element_tree, form, here, extra_context,
                         ooo_builder, iteration_index=0):
-
     field_list = form.get_fields(include_disabled=1)
     for (count, field) in enumerate(field_list):
-      text_xpath = '//draw:frame[@draw:name="%s"]/*' % field.id
+      text_xpath = '//draw:frame[@draw:name="%s"]' % field.id
       node_list = element_tree.xpath(text_xpath, namespaces=element_tree.nsmap)
+
       for target_node in node_list:
         attr_dict = {}
+        attr_dict.setdefault(target_node.tag, {}).update(target_node.attrib)
         # store child style using their local-name as key
         for descendant in target_node.iterdescendants():
           attr_dict.setdefault(descendant.tag, {}).update(descendant.attrib)
-        new_node = field.render_odg(attr_dict=attr_dict)
-        parent_node = target_node.getparent().replace(target_node, new_node)
+        # render the field in odg xml node format
+        new_node = field.render_odg(target_node=target_node, printout=self,
+            REQUEST=self.REQUEST, ooo_builder=ooo_builder, attr_dict=attr_dict)
+        if new_node is not None:
+          target_node.getparent().replace(target_node, new_node)
+        else:
+          # if the render return None, remove the node
+          target_node.getparent().remove(target_node)

Modified: erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODG.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODG.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODG.py [utf8] (original)
+++ erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODG.py [utf8] Wed Dec 16 16:23:14 2009
@@ -80,6 +80,9 @@
     if custom._getOb('Foo_viewAsODGPrintout', None) is None:
       erp5OOo.addFormPrintout(id='Foo_viewAsODGPrintout', title='',
                               form_name='Foo_view', template='Foo_getODGStyleSheet')
+    if custom._getOb('Foo_viewProxyFieldAsODGPrintout', None) is None:
+      erp5OOo.addFormPrintout(id='Foo_viewProxyFieldAsODGPrintout', title='',
+                              form_name='Foo_viewProxyField', template='Foo_getODGStyleSheet')
     if custom._getOb('FooReport_viewAsODGPrintout', None) is None:
       erp5OOo.addFormPrintout(id='FooReport_viewAsODGPrintout',
                               title='')
@@ -363,6 +366,126 @@
     self.assertTrue(content_xml.find('<draw:image xlink:href') < 0)
     self._validate(odf_document)
 
+  def test_04_ProxyField(self):
+    """
+    Check it's possible to use an odg document to map proxyfields
+    """
+    portal = self.getPortal()
+    foo_module = self.portal.foo_module
+    if foo_module._getOb('test1', None) is None:
+      foo_module.newContent(id='test1', portal_type='Foo')
+    test1 =  foo_module.test1
+    test1.setTitle('Foo title!')
+    transaction.commit()
+    self.tic()
+
+    style_dict = {'{urn:oasis:names:tc:opendocument:xmlns:text:1.0}span':
+                    {'{urn:oasis:names:tc:opendocument:xmlns:text:1.0}style-name': 'T2'},
+                  '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}p': {}
+                 }
+
+    # test target
+    foo_printout = portal.foo_module.test1.Foo_viewProxyFieldAsODGPrintout
+    original_file_content = self.getODFDocumentFromPrintout(foo_printout)
+    self._validate(original_file_content)
+
+    # extract content.xml from original odg document
+    original_doc_builder = OOoBuilder(original_file_content)
+    original_content_xml = original_doc_builder.extract("content.xml")
+    # get style of the title in the orignal test document
+    original_document_style_dict = self.getStyleDictFromFieldName(original_content_xml,
+        'my_title')
+
+    # check the style is good before the odg generation
+    self.assertEqual(original_document_style_dict, style_dict)
+
+    request = self.app.REQUEST
+    # 1. Normal case: "my_title" field to the "my_title" reference in the ODF document
+    odf_document = foo_printout.index_html(REQUEST=request)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    final_document_style_dict = self.getStyleDictFromFieldName(content_xml,
+        'my_title')
+
+    # check the style is keept after the odg generation
+    self.assertEqual(final_document_style_dict, style_dict)
+
+    self.assertTrue(content_xml.find("Foo title!") > 0)
+    self.assertEqual(request.RESPONSE.getHeader('content-type'),
+                     'application/vnd.oasis.opendocument.graphics; charset=utf-8')
+    self.assertEqual(request.RESPONSE.getHeader('content-disposition'),
+                     'inline;filename="Foo_viewProxyFieldAsODGPrintout.odg"')
+    self._validate(odf_document)
+
+    # 2. Normal case: change the field value and check again the ODF document
+    test1.setTitle("Changed Title!")
+    #foo_form.my_title.set_value('default', "Changed Title!")
+    odf_document = foo_printout.index_html(REQUEST=request)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(content_xml.find("Changed Title!") > 0)
+    self._validate(odf_document)
+
+    # 3. False case: change the field name
+    test1.setTitle("you cannot find")
+    # rename id 'my_title' to 'xxx_title', then does not match in the ODF document
+    foo_form = portal.foo_module.test1.Foo_viewProxyField
+    foo_form.manage_renameObject('my_title', 'xxx_title', REQUEST=request)
+    odf_document = foo_printout.index_html(REQUEST=request)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertFalse(content_xml.find("you cannot find") > 0)
+    self._validate(odf_document)
+    # put back
+    foo_form.manage_renameObject('xxx_title', 'my_title', REQUEST=request)
+
+    ## 4. False case: does not set a ODF template
+    self.assertTrue(foo_printout.template == 'Foo_getODGStyleSheet')
+    tmp_template = foo_printout.template
+    foo_printout.template = None
+    # template == None, causes a ValueError
+    try:
+      foo_printout.index_html(REQUEST=request)
+    except ValueError, e:
+      # e -> 'Can not create a ODF Document without a odf_template'
+      self.assertTrue(True)
+
+    # put back
+    foo_printout.template = tmp_template
+
+    # 5. Normal case: just call a FormPrintout object
+    request.RESPONSE.setHeader('Content-Type', 'text/html')
+    test1.setTitle("call!")
+    odf_document = foo_printout() # call
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(content_xml.find("call!") > 0)
+    # when just call FormPrintout, it does not change content-type
+    self.assertEqual(request.RESPONSE.getHeader('content-type'), 'text/html')
+    self._validate(odf_document)
+
+    # 5. Normal case: utf-8 string
+    test1.setTitle("Français")
+    odf_document = foo_printout()
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(content_xml.find("Français") > 0)
+    self._validate(odf_document)
+
+    # 6. Normal case: unicode string
+    test1.setTitle(u'Français test2')
+    odf_document = foo_printout()
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(content_xml.find("Français test2") > 0)
+    self._validate(odf_document)
+
 def test_suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestFormPrintoutAsODG))

Modified: erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODT.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODT.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODT.py [utf8] (original)
+++ erp5/trunk/products/ERP5OOo/tests/testFormPrintoutAsODT.py [utf8] Wed Dec 16 16:23:14 2009
@@ -1111,8 +1111,6 @@
     # 01: Normal
     odf_document = foo_printout()
     self.assertTrue(odf_document is not None)
-    #test_output = open("/tmp/test_07_Image_01_Normal.odf", "w")
-    #test_output.write(odf_document)
     builder = OOoBuilder(odf_document)
     content_xml = builder.extract("content.xml")
     self.assertTrue(content_xml.find("test_image.png") > 0)
@@ -1131,8 +1129,6 @@
     my_default_image_absolute_url.values['default'] = ''
     odf_document = foo_printout()
     self.assertTrue(odf_document is not None)
-    #test_output = open("/tmp/test_07_Image_02_NoData.odf", "w")
-    #test_output.write(odf_document)
     builder = OOoBuilder(odf_document)
     content_xml = builder.extract("content.xml")
     # confirming the image was removed

Modified: erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odg
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odg?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
Binary files - no diff available.

Modified: erp5/trunk/products/Formulator/Field.py
URL: http://svn.erp5.org/erp5/trunk/products/Formulator/Field.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/Formulator/Field.py [utf8] (original)
+++ erp5/trunk/products/Formulator/Field.py [utf8] Wed Dec 16 16:23:14 2009
@@ -284,8 +284,10 @@
       return self.widget.render_odt(self, as_string, local_name, attr_dict=attr_dict)
 
     security.declareProtected('View', 'render_odg')
-    def render_odg(self, as_string=False, local_name='p', attr_dict=None):
-      return self.widget.render_odg(self, as_string, local_name, attr_dict=attr_dict)
+    def render_odg(self, as_string=False, local_name='p', target_node=None,
+        printout=None, REQUEST=None, ooo_builder=None, attr_dict=None):
+      return self.widget.render_odg(self, as_string, local_name, target_node,
+          printout, REQUEST, ooo_builder, attr_dict)
 
     security.declareProtected('View', 'render_css')
     def render_css(self, REQUEST=None):

Modified: erp5/trunk/products/Formulator/Widget.py
URL: http://svn.erp5.org/erp5/trunk/products/Formulator/Widget.py?rev=31339&r1=31338&r2=31339&view=diff
==============================================================================
--- erp5/trunk/products/Formulator/Widget.py [utf8] (original)
+++ erp5/trunk/products/Formulator/Widget.py [utf8] Wed Dec 16 16:23:14 2009
@@ -190,10 +190,11 @@
       return etree.tostring(text_node)
     return text_node
 
-  def render_odg(self, field, as_string, local_name, attr_dict=None):
+  def render_odg(self, field, as_string, local_name, target_node=None,
+      printout=None, REQUEST=None, ooo_builder=None, attr_dict=None):
     """
       Default render odg for widget - to be overwritten in field classes.
-      Return a field value rendered in odg format.
+      Return a field node rendered in odg format.
       if as_string is True (default) the returned value is a string (xml
       reprensation of the node), if it's False, the value returned is the node
       object.
@@ -211,17 +212,25 @@
     value = '\n'.join(value)
     value.replace('\r', '')
 
+    draw_frame_tag_name = '{%s}%s' % (DRAW_URI, 'frame')
+    draw_frame_node = Element(draw_frame_tag_name, nsmap=NSMAP)
+    draw_frame_node.attrib.update(attr_dict.get(draw_frame_tag_name, {}))
+
     draw_tag_name = '{%s}%s' % (DRAW_URI, 'text-box')
     draw_node = Element(draw_tag_name, nsmap=NSMAP)
     draw_node.attrib.update(attr_dict.get(draw_tag_name, {}))
+
     text_p_tag_name = '{%s}%s' % (TEXT_URI, local_name)
     text_p_node = Element(text_p_tag_name, nsmap=NSMAP)
     text_p_node.attrib.update(attr_dict.get(text_p_tag_name, {}))
+
     text_span_tag_name = '{%s}%s' % (TEXT_URI, 'span')
     text_span_node =  Element(text_span_tag_name, nsmap=NSMAP)
     text_span_node.attrib.update(attr_dict.get(text_span_tag_name, {}))
+
     text_p_node.append(text_span_node)
     draw_node.append(text_p_node)
+    draw_frame_node.append(draw_node)
 
     # XXX copy from render_odt, need to be unified
     def replaceCharsByNode(match_object):
@@ -235,11 +244,9 @@
             line_break = SubElement(text_span_node, '{%s}%s' % (TEXT_URI, 'tab'))
             line_break.tail = match_object.group(2)
     re.sub('([\n\t])?([^\n\t]*)', replaceCharsByNode, value)
-    #text_span_node.text = value
     if as_string:
-      return etree.tostring(draw_node)
-    return draw_node
-
+      return etree.tostring(draw_frame_node)
+    return draw_frame_node
 
 class TextWidget(Widget):
   """Text widget




More information about the Erp5-report mailing list