[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