[Erp5-report] r26251 - in /erp5/trunk/products/ERP5OOo: ./ dtml/ tests/ tests/test_document...

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Apr 2 12:35:35 CEST 2009


Author: tatuya
Date: Thu Apr  2 12:35:29 2009
New Revision: 26251

URL: http://svn.erp5.org?rev=26251&view=rev
Log:
The Form Printout enables to create a ODF document,
using an ERP5 Form and an ODF template.

I and yo had long discussion, whether to invoke
an ODF template or an ERP5 form.
We selected an ERP5 Form to invoke, but because of 
some point of view, we changed the choice and decided
to create a new class to invoke to create a ODF document.

Why we decided to create a new class:

* Reusable of forms 
  If we invoke an ERP5 form to create an ODF document, we have to
  set a ODF template, and the view of the form fixes ODF.
  Thus the form can not use other cases such as html view.
  
* Consistency 
  Set a ODF template as well as page template, the forms has
  two ways two template. It has not consistency.

The Form Printout currently supports only ODT format document.
And the functions only supports paragraphs and tables.


Added:
    erp5/trunk/products/ERP5OOo/FormPrintout.py
    erp5/trunk/products/ERP5OOo/dtml/FormPrintout_add.dtml
    erp5/trunk/products/ERP5OOo/tests/testFormPrintout.py
    erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odt   (with props)
    erp5/trunk/products/ERP5OOo/www/FormPrintout_manageEdit.zpt
Modified:
    erp5/trunk/products/ERP5OOo/__init__.py

Added: erp5/trunk/products/ERP5OOo/FormPrintout.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/FormPrintout.py?rev=26251&view=auto
==============================================================================
--- erp5/trunk/products/ERP5OOo/FormPrintout.py (added)
+++ erp5/trunk/products/ERP5OOo/FormPrintout.py [utf8] Thu Apr  2 12:35:29 2009
@@ -1,0 +1,549 @@
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi KK and Contributors. All Rights Reserved.
+#                    Tatuya Kamada <tatuya at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Products.CMFCore.utils import _checkPermission, getToolByName    
+from Products.ERP5Type import PropertySheet, Permissions
+from Products.ERP5Form.ListBox import ListBox 
+from Products.ERP5OOo.OOoUtils import OOoBuilder
+
+from Acquisition import Implicit
+from Globals import InitializeClass, DTMLFile, Persistent, get_request
+from AccessControl import ClassSecurityInfo
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item, SimpleItem
+from urllib import quote
+from copy import deepcopy
+from lxml import etree
+from zLOG import LOG, DEBUG, INFO, WARNING
+from mimetypes import guess_extension
+
+try:
+  from webdav.Lockable import ResourceLockedError
+  from webdav.WriteLockInterface import WriteLockInterface
+  SUPPORTS_WEBDAV_LOCKS = 1
+except ImportError:
+  SUPPORTS_WEBDAV_LOCKS = 0
+
+# Constructors
+manage_addFormPrintout = DTMLFile("dtml/FormPrintout_add", globals())
+
+def addFormPrintout(self, id, title="", form_name='', template='', REQUEST=None):
+  """Add form printout to folder.
+
+  Keyword arguments:
+  id     -- the id of the new form printout to add
+  title  -- the title of the form printout to add
+  form_name -- the name of a form which contains data to printout
+  template -- the name of a template which describes printout layout
+  """
+  # add actual object
+  id = self._setObject(id, FormPrintout(id, title, form_name, template))
+  # respond to the add_and_edit button if necessary
+  add_and_edit(self, id, REQUEST)
+  return ''
+
+def add_and_edit(self, id, REQUEST):
+  """Helper method to point to the object's management screen if
+  'Add and Edit' button is pressed.
+
+  Keyword arguments:
+  id -- id of the object we just added
+  """
+  if REQUEST is None:
+    return
+  try:
+    u = self.DestinationURL()
+  except AttributeError:
+    u = REQUEST['URL1']
+  if REQUEST['submit'] == " Add and Edit ":
+    u = "%s/%s" % (u, quote(id))
+  REQUEST.RESPONSE.redirect(u+'/manage_main')
+  
+class FormPrintout(Implicit, Persistent, RoleManager, Item):
+  """Form Printout
+
+  The Form Printout enables to create a ODF document.
+
+  The Form Printout receives a name of a ERP5 form, and a template name.
+  Using their parameters, the Form Printout genereate a ODF document,
+  a form as a ODF document content, and a template as a document layout.
+
+  WARNING: The Form Printout currently supports only ODT format document.
+  And the functions only supports paragraphs and tables.
+  
+  Fields <-> Paragraphs:      supported
+  ListBox <-> Table:          supported
+  Report Section <-> Tables:  experimentally supported
+  FormBox <-> Frame:          not supported yet
+  Photo <-> Image:            not supported yet
+  Style group <-> styles.xml: not supported yet
+  Meta group <-> meta.xml:    not supported yet
+  """
+  
+  meta_type = "ERP5 Form Printout"
+
+  # Declarative Security
+  security = ClassSecurityInfo()
+
+  # Declarative properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.SimpleItem)
+
+  # Constructors
+  constructors =   (manage_addFormPrintout, addFormPrintout)
+
+  # Tabs in ZMI
+  manage_options = ((
+    {'label':'Edit', 'action':'manage_editFormPrintout'},
+    {'label':'View', 'action': '' }, ) + Item.manage_options)
+                    
+
+  security.declareProtected('View management screens', 'manage_editFormPrintout')
+  manage_editFormPrintout = PageTemplateFile('www/FormPrintout_manageEdit', globals(),
+                                  __name__='manage_editFormPrintout')
+  manage_editFormPrintout._owner = None
+  
+  # default attributes
+  template = None
+  form_name = None
+
+  def __str__(self): return self.index_html()
+
+  def __len__(self): return 1
+
+  def __init__(self, id, title='', form_name='', template=''):
+    """Initialize id, title, form_name, template.
+
+    Keyword arguments:
+    id -- the id of a form printout
+    title -- the title of a form printout
+    form_name -- the name of a form which as a document content
+    template -- the name of a template which as a document layout
+    """
+    self.id = id
+    self.title = title
+    self.form_name = form_name
+    self.template = template
+
+  security.declareProtected('View management screens', 'index_html')
+  def index_html(self, icon=0, preview=0, width=None, height=None, REQUEST=None):
+    """Render and view a printout document."""
+    
+    obj = getattr(self, 'aq_parent', None)
+    if obj is not None:
+      container = obj.aq_inner.aq_parent
+      if not _checkPermission(Permissions.View, obj):
+        raise AccessControl_Unauthorized('This document is not authorized for view.')
+      else:
+        container = None
+    form = getattr(obj, self.form_name)
+    if self.template is None or self.template == '':
+      raise ValueError, 'Can not create a ODF Document without a printout template'
+    printout_template = getattr(obj, self.template)
+
+    report_method = None
+    if hasattr(form, 'report_method'):
+      report_method = getattr(obj, form.report_method)
+    extra_context = dict( container=container,
+                          printout_template=printout_template,
+                          report_method=report_method,
+                          form=form,
+                          here=obj )
+    # set property to aquisition
+    content_type = printout_template.content_type
+    self.strategy = self._create_strategy(content_type)
+    printout = self.strategy.render(extra_context=extra_context)
+    REQUEST.RESPONSE.setHeader('Content-Type','%s; charset=utf-8' % content_type)
+    REQUEST.RESPONSE.setHeader('Content-disposition',
+                               'inline;filename="%s%s"' % (self.title_or_id(), guess_extension(content_type)))
+    return printout
+ 
+  security.declareProtected('Manage properties', 'doSettings')
+  def doSettings(self, REQUEST, title='', form_name='', template=''):
+    """Change title, form_name, template."""
+    if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
+      raise ResourceLockedError, "File is locked via WebDAV"
+    self.form_name = form_name
+    self.template = template
+    self.title = title
+    message = "Saved changes."
+    if getattr(self, '_v_warnings', None):
+      message = ("<strong>Warning:</strong> <i>%s</i>"
+                % '<br>'.join(self._v_warnings))
+    return self.manage_editFormPrintout(manage_tabs_message=message)
+
+  def _create_strategy(slef, content_type=''):
+    if guess_extension(content_type) == '.odt':
+      return ODTStrategy()
+    raise ValueError, 'Do not support the template type:%s' % content_type
+
+InitializeClass(FormPrintout)
+
+
+NAME_SPACE_DICT = {'draw':'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0',
+                   'table':'urn:oasis:names:tc:opendocument:xmlns:table:1.0',
+                   'text':'urn:oasis:names:tc:opendocument:xmlns:text:1.0',
+                   'office':'urn:oasis:names:tc:opendocument:xmlns:office:1.0'}
+
+class ODFStrategy(Implicit):
+  """ODFStrategy creates a ODF Document. """
+  
+  def render(self, extra_context={}):
+    """Render a odt document, form as a content, template as a template.
+
+    Keyword arguments:
+    extra_context -- a dictionary, guess contains
+                     'here' : where it call
+                     'printout_template' : the template object, tipically a OOoTemplate
+                     'container' : the object which has a form printout object
+                     'form' : the form as a content
+    """
+    here = extra_context['here']
+    if here is None:
+      # This is a system error
+      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:
+      raise ValueError, 'Can not create a ODF Document without a printout template'
+
+    odf_template = extra_context['printout_template']
+    
+    # First, render a OOoTemplate itself with its style
+    ooo_document = None
+    if hasattr(odf_template, 'pt_render'):
+      ooo_document = odf_template.pt_render(here, extra_context=extra_context)
+    else:
+      # File object, OOoBuilder directly support a file object
+      ooo_document = odf_template 
+
+    # Create a new builder instance
+    ooo_builder = OOoBuilder(ooo_document)
+
+    # content.xml
+    ooo_builder = self._replace_content_xml(ooo_builder=ooo_builder, extra_context=extra_context)
+    # styles.xml and meta.xml are not supported yet
+    # ooo_builder = self._replace_styles_xml(ooo_builder=ooo_builder, extra_context=extra_context)
+    # ooo_builder = self._replace_meta_xml(ooo_builder=ooo_builder, extra_context=extra_context)
+        
+    # Update the META informations
+    ooo_builder.updateManifest()
+
+    ooo = ooo_builder.render(name=odf_template.title or odf_template.id)
+    return ooo
+
+  def _replace_content_xml(self, ooo_builder=None, extra_context=None):
+    doc_xml = ooo_builder.extract('content.xml')
+    # mapping ERP5Form to ODF
+    form = extra_context['form']
+    here = getattr(self, 'aq_parent', None)
+    ordinaly_group_list = [group for group in form.get_groups() if group not in ['meta','style']]
+    doc_xml = self._replace_xml_by_form(doc_xml=doc_xml,
+                                        form=form,
+                                        here=here,
+                                        extra_context=extra_context,
+                                        group_list=ordinaly_group_list)
+    # mapping ERP5Report report method to ODF
+    doc_xml = self._replace_xml_by_report_section(doc_xml=doc_xml,
+                                                  extra_context=extra_context)
+
+    if isinstance(doc_xml, unicode):
+      doc_xml = doc_xml.encode('utf-8')
+ 
+    # Replace content.xml in master openoffice template
+    ooo_builder.replace('content.xml', doc_xml)
+    return ooo_builder
+
+  # this method not supported yet
+  def _replace_styles_xml(self, ooo_builder=None, extra_context=None):
+    """
+    replacing styles.xml file in a ODF document
+    """
+    doc_xml = ooo_builder.extract('styles.xml')
+    form = extra_context['form']
+    here = getattr(self, 'aq_parent', None)
+    doc_xml = self._replace_xml_by_form(doc_xml=doc_xml,
+                                        form=form,
+                                        here=here,
+                                        extra_context=extra_context,
+                                        group_list=['styles'])
+
+    if isinstance(doc_xml, unicode):
+      doc_xml = doc_xml.encode('utf-8')
+ 
+    ooo_builder.replace('styles.xml', doc_xml)
+    return ooo_builder
+
+  # this method not implemented yet
+  def _replace_meta_xml(self, ooo_builder=None, extra_content=None):
+    """
+    replacing meta.xml file in a ODF document
+    """
+    doc_xml = ooo_builder.extract('meta.xml')
+
+    if isinstance(doc_xml, unicode):
+      doc_xml = doc_xml.encode('utf-8')
+ 
+    ooo_builder.replace('meta.xml', doc_xml)
+    return ooo_builder
+
+  def _replace_xml_by_form(self, doc_xml=None, form=None, here=None,
+                           group_list=[], extra_context=None):
+    field_list = [f for g in group_list for f in form.get_fields_in_group(g)]
+    content = etree.XML(doc_xml)
+    REQUEST = get_request()
+    for (count,field) in enumerate(field_list):
+      if isinstance(field, ListBox):
+        content = self._append_table_by_listbox(content=content,
+                                                listbox=field,
+                                                REQUEST=REQUEST) 
+      else:
+        field_value = field.get_value('default')
+        content = self._replace_node_via_reference(content=content,
+                                                   field_id=field.id,
+                                                   field_value=field_value)
+    return etree.tostring(content)
+
+  def _replace_node_via_reference(self, content=None, field_id=None, field_value=None):
+    # text:reference-mark text:name="invoice-date"
+    reference_xpath = '//text:reference-mark[@text:name="%s"]' % field_id
+    reference_list = content.xpath(reference_xpath, namespaces=NAME_SPACE_DICT)
+    if len(reference_list) > 0:
+      node = reference_list[0].getparent()
+      ## remove such a "bbb"
+      ## <text:p>aaa<br/>bbb</text:p>
+      for child in node.getchildren():
+        child.tail = ''
+      node.text = field_value
+    return content
+  
+  def _replace_xml_by_report_section(self, doc_xml=None, extra_context=None):
+    if not extra_context.has_key('report_method') or extra_context['report_method'] is None:
+      return doc_xml
+    report_method = extra_context['report_method']
+    report_section_list = report_method()
+    portal_object = self.getPortalObject()
+    content = etree.XML(doc_xml)
+    REQUEST = get_request()
+    request = extra_context.get('REQUEST', REQUEST)
+
+    render_prefix = None
+    for (index, report_item) in enumerate(report_section_list):
+      if index > 0:
+        render_prefix = 'x%s' % index
+      report_item.pushReport(portal_object, render_prefix = render_prefix)
+      here = report_item.getObject(portal_object)
+      form_id = report_item.getFormId()
+      form = getattr(here, form_id)
+      for field in form.get_fields():
+        if field.meta_type == 'ListBox':
+          content = self._append_table_by_listbox(content=content,
+                                                  listbox=field,
+                                                  REQUEST=request,
+                                                  render_prefix=render_prefix) 
+      report_item.popReport(portal_object, render_prefix = render_prefix)
+   
+    return etree.tostring(content)
+
+  def _append_table_by_listbox(self,
+                               content=None, 
+                               listbox=None,
+                               REQUEST=None,
+                               render_prefix=None):
+    table_id = listbox.id
+    table_xpath = '//table:table[@table:name="%s"]' % table_id
+    # this list should be one item list
+    target_table_list = content.xpath(table_xpath, namespaces=NAME_SPACE_DICT)
+    if len(target_table_list) is 0:
+      return content
+
+    target_table = target_table_list[0]
+    newtable = deepcopy(target_table)
+    # <table:table-header-rows>
+    table_header_rows_xpath = '%s/table:table-header-rows' % table_xpath
+    table_row_xpath = '%s/table:table-row' % table_xpath
+    table_header_rows_list = newtable.xpath(table_header_rows_xpath,  namespaces=NAME_SPACE_DICT)
+    table_row_list = newtable.xpath(table_row_xpath,  namespaces=NAME_SPACE_DICT)
+
+    # copy row styles from ODF Document
+    has_header_rows = len(table_header_rows_list) > 0
+    LOG('FormPrintout has_header_rows', INFO, has_header_rows)
+    (row_top, row_middle, row_bottom) = self._copy_row_style(table_row_list,
+                                                             has_header_rows=has_header_rows)
+
+    # clear original table 
+    parent_paragraph = target_table.getparent()
+    target_index = parent_paragraph.index(target_table)
+    # 'render_prefix is None' means it is the first table of iteration
+    if render_prefix is None:
+      parent_paragraph.remove(target_table)
+    # clear rows 
+    for table_row in table_row_list:
+      newtable.remove(table_row)
+
+    listboxline_list = listbox.get_value('default',
+                                         render_format='list',
+                                         REQUEST=REQUEST, 
+                                         render_prefix=render_prefix)
+    
+    # if ODF table has heder rows, does not update the header rows
+    # if ODF table does not have header rows, insert the listbox title line
+    is_top = True
+    for (index, listboxline) in enumerate(listboxline_list):
+      listbox_column_list = listboxline.getColumnItemList()
+      if listboxline.isTitleLine() and not has_header_rows:
+        row = deepcopy(row_top)
+        row = self._update_column_value(row, listbox_column_list)
+        newtable.append(row)
+        is_top = False       
+      elif listboxline.isDataLine() and is_top:
+        row = deepcopy(row_top)
+        row = self._update_column_value(row, listbox_column_list)
+        newtable.append(row)
+        is_top = False
+      elif index > 0 and listboxline.isDataLine():
+        row = deepcopy(row_middle)
+        row = self._update_column_value(row, listbox_column_list)
+        newtable.append(row)
+      elif listboxline.isStatLine():
+        row = deepcopy(row_bottom)
+        row = self._update_column_stat_value(row, listbox_column_list, row_middle)
+        newtable.append(row)
+
+    # direct listbox mapping
+    if render_prefix is None:
+      parent_paragraph.insert(target_index, newtable)
+    else:
+      # report section iteration
+      parent_paragraph.append(newtable)
+      # TODO: it would be better append a paragraph or linebreak
+      
+    return content
+
+  def _copy_row_style(self, table_row_list=[], has_header_rows=False):
+    row_top = None
+    row_middle = None
+    row_bottom = None
+    if len(table_row_list) > 0:
+      if len(table_row_list) is 1:
+        row_top = deepcopy(table_row_list[0])
+        row_middle = deepcopy(table_row_list[0])
+        row_bottom = deepcopy(table_row_list[0])
+      elif len(table_row_list) is 2 and has_header_rows:
+        row_top = deepcopy(table_row_list[0])
+        row_middle = deepcopy(table_row_list[0])
+        row_bottom = deepcopy(table_row_list[-1])
+      elif len(table_row_list) is 2 and not has_header_rows:
+        row_top = deepcopy(table_row_list[0])
+        row_middle = deepcopy(table_row_list[1])
+        row_bottom = deepcopy(table_row_list[-1])
+      elif len(table_row_list) >= 2:
+        row_top = deepcopy(table_row_list[0])
+        row_middle = deepcopy(table_row_list[1])
+        row_bottom = deepcopy(table_row_list[-1])
+    return (row_top, row_middle, row_bottom)
+  
+  def _update_column_value(self, row=None, listbox_column_list=[]):
+    odf_cell_list = row.findall("{%s}table-cell" % NAME_SPACE_DICT['table'])
+    odf_cell_list_size = len(odf_cell_list)
+    listbox_column_size = len(listbox_column_list)
+    for (column_index, column) in enumerate(odf_cell_list):
+      if column_index >= listbox_column_size:
+        break
+      value = listbox_column_list[column_index][1]
+      self._set_column_value(column, value)
+    return row
+
+  def _update_column_stat_value(self, row=None, listbox_column_list=[], row_middle=None):
+    if row_middle is None:
+      return row
+    odf_cell_list = row.findall("{%s}table-cell" % NAME_SPACE_DICT['table'])
+    odf_column_span_list = self._get_odf_column_span_list(row_middle)
+    listbox_column_size = len(listbox_column_list)
+    listbox_column_index = 0
+    for (column_index, column) in enumerate(odf_cell_list):
+      if listbox_column_index >= listbox_column_size:
+        break
+      value = listbox_column_list[listbox_column_index][1]
+      # if value is None, remaining ODF orinal text
+      if value is not None:
+        self._set_column_value(column, value)
+      column_span = self._get_column_span_size(column)
+      listbox_column_index = self._next_listbox_column_index(column_span,
+                                                             listbox_column_index,
+                                                             odf_column_span_list)
+    return row
+
+  def _set_column_value(self, column, value):
+    if value is None:
+      # to eliminate a default value, remove "office:*" attributes.
+      # if remaining "office:*" attribetes, the column shows its default value,
+      # such as '0.0', '$0'
+      attrib = column.attrib
+      for key in attrib.keys():
+        if key.startswith("{%s}" % NAME_SPACE_DICT['office']):
+          del attrib[key]
+        value = ''
+    column_value = unicode(str(value),'utf-8')
+    column.text = column_value
+    column_children = column.getchildren()
+    if len(column_children) > 0:
+      column_children[0].text = column_value
+    if column_value != '':
+      column.set("{%s}value" % NAME_SPACE_DICT['office'], column_value)
+    
+  def _get_column_span_size(self, column=None):
+    span_attribute = "{%s}number-columns-spanned" % NAME_SPACE_DICT['table']
+    column_span = 1
+    if column.attrib.has_key(span_attribute):
+      column_span = int(column.attrib[span_attribute])
+    return column_span
+  
+  def _next_listbox_column_index(self, span=0, current_index=0, column_span_list=[]):
+    hops = 0
+    index = current_index
+    while hops < span:
+      column_span = column_span_list[index]
+      hops += column_span
+      index += 1
+    return index
+  
+  def _get_odf_column_span_list(self, row_middle=None):
+    if row_middle is None:
+      return []
+    odf_cell_list = row_middle.findall("{%s}table-cell" % NAME_SPACE_DICT['table'])
+    column_span_list = []
+    span_attribute = "{%s}number-columns-spanned" % NAME_SPACE_DICT['table']
+    for column in odf_cell_list:
+      column_span = self._get_column_span_size(column)
+      column_span_list.append(column_span)
+    return column_span_list
+
+class ODTStrategy(ODFStrategy):
+  """ODTStrategy create a ODT Document from a form and a ODT template"""
+  pass

Modified: erp5/trunk/products/ERP5OOo/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/__init__.py?rev=26251&r1=26250&r2=26251&view=diff
==============================================================================
--- erp5/trunk/products/ERP5OOo/__init__.py [utf8] (original)
+++ erp5/trunk/products/ERP5OOo/__init__.py [utf8] Thu Apr  2 12:35:29 2009
@@ -45,7 +45,8 @@
 
 # Define object classes and tools
 from OOoTemplate import OOoTemplate
-object_classes = (OOoTemplate, )
+from FormPrintout import FormPrintout
+object_classes = (OOoTemplate, FormPrintout)
 portal_tools = ()
 content_classes = ()
 content_constructors = ()

Added: erp5/trunk/products/ERP5OOo/dtml/FormPrintout_add.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/dtml/FormPrintout_add.dtml?rev=26251&view=auto
==============================================================================
--- erp5/trunk/products/ERP5OOo/dtml/FormPrintout_add.dtml (added)
+++ erp5/trunk/products/ERP5OOo/dtml/FormPrintout_add.dtml [utf8] Thu Apr  2 12:35:29 2009
@@ -1,0 +1,73 @@
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+           form_title='Add ERP5 Form Blueprint',
+           )">
+
+<p class="form-help">
+
+</p>
+
+<form action="addFormPrintout" method="POST">
+
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Id
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="id" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Title
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="title" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Form Name
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="form_name" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Template
+    </div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="template" size="40" />
+    </td>
+  </tr>
+
+  <tr>
+    <td align="left" valign="top">
+    </td>
+    <td align="left" valign="top">
+    <div class="form-element">
+    <input class="form-element" type="submit" name="submit" 
+     value=" Add " /> 
+    <input class="form-element" type="submit" name="submit" 
+     value=" Add and Edit " />
+    </div>
+    </td>
+  </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>

Added: erp5/trunk/products/ERP5OOo/tests/testFormPrintout.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/tests/testFormPrintout.py?rev=26251&view=auto
==============================================================================
--- erp5/trunk/products/ERP5OOo/tests/testFormPrintout.py (added)
+++ erp5/trunk/products/ERP5OOo/tests/testFormPrintout.py [utf8] Thu Apr  2 12:35:29 2009
@@ -1,0 +1,373 @@
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi KK and Contributors. All Rights Reserved.
+#                    Tatuya Kamada <tatuya at nexedi.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+import unittest
+from threading import Thread
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from AccessControl.SecurityManagement import newSecurityManager
+from Products.ERP5Form.Selection import Selection
+from Products.ERP5Form.SelectionTool import SelectionTool
+from Products.ERP5OOo.OOoUtils import OOoBuilder
+from zLOG import LOG , INFO
+import os
+
+class TestFormPrintout(ERP5TypeTestCase):
+  quiet = 1
+  run_all_test = 1
+
+  def getTitle(self):
+    """
+      Return the title of the current test set.
+    """
+    return "FormPrintout"
+
+  def getBusinessTemplateList(self):
+    return ('erp5_ui_test', 'erp5_odt_style')
+
+  def afterSetUp(self):
+    self.login()
+    foo_file_path = os.path.join(os.path.dirname(__file__),
+                                'test_document',
+                                'Foo_001.odt')
+    foo_file = open(foo_file_path, 'rb')
+    custom = self.portal.portal_skins.custom
+    addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
+    addStyleSheet(id='Foo_getODTStyleSheet', file=foo_file, title='',
+                  precondition='', content_type = 'application/vnd.oasis.opendocument.text')
+    erp5OOo = custom.manage_addProduct['ERP5OOo']
+    addOOoTemplate = erp5OOo.addOOoTemplate
+    addOOoTemplate(id='Foo_viewAsOdt', title='')
+    request = self.app.REQUEST
+    Foo_viewAsOdt = custom.Foo_viewAsOdt
+    Foo_viewAsOdt.doSettings(request, title='', xml_file_id='content.xml',
+                             ooo_stylesheet='Foo_getODTStyleSheet')
+    #Foo_viewAsOdt.pt_upload(request, file=foo_file)
+    #render_result = Foo_viewAsOdt(REQUEST=request)
+    #LOG('testFormPrintout render_result:', INFO, render_result)
+    builder = OOoBuilder(foo_file)
+    content = builder.extract('content.xml')
+    Foo_viewAsOdt.pt_edit(content, content_type='application/vnd.oasis.opendocument.text')
+
+    erp5OOo.addFormPrintout(id='Foo_viewAsPrintout', title='',
+                            form_name='Foo_view', template='Foo_getODTStyleSheet')
+
+  def login(self):
+    uf = self.getPortal().acl_users
+    uf._doAddUser('tatuya', '', ['Manager'], [])
+    user = uf.getUserById('tatuya').__of__(uf)
+    newSecurityManager(None, user)
+                          
+  def _test_01_Paragraph(self, quiet=0, run=run_all_test):
+    """
+    mapping a field to a paragraph
+    """
+    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!')
+    get_transaction().commit()
+    self.tic()
+
+    # test target
+    foo_printout = portal.foo_module.test1.Foo_viewAsPrintout
+
+    request = self.app.REQUEST
+    # 1. Normal case: "my_title" field to the "my_title" reference in the ODF document
+    self.portal.changeSkin('ODT')
+    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("Foo title!") > 0)
+
+    # 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)
+
+    # 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_view
+    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)
+    # 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_getODTStyleSheet')
+    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)
+
+
+  def test_02_Table(self, quiet=0, run=run_all_test):
+    """
+    To test mapping a listbox(Form) to a table(ODF)
+    """
+    portal = self.getPortal()
+    ## append 'test1' data to a listbox
+    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
+    if test1._getOb("foo_1", None) is None:
+      test1.newContent("foo_1", portal_type='Foo Line')
+    get_transaction().commit()
+    self.tic()
+    # test target
+    foo_printout = portal.foo_module.test1.Foo_viewAsPrintout
+
+    # Test Data format
+    #
+    # * ODF table named 'listbox':
+    # +------------------------------+
+    # |  ID | Title | Quantity |Date |
+    # |-----+-------+----------+-----|
+    # |     |       |          |     |
+    # |-----+-------+----------+-----|
+    # |   Total     |          |     |
+    # +------------------------------+
+    #
+    # * ODF table named 'listbox2':
+    # +-------------------------------+
+    # |  A    |   B   |   C   |   D   |
+    # |-------+-------+-------+-------|
+    # |       |       |       |       |
+    # +-------+-------+-------+-------+
+    #
+    # * ODF table named 'listbox3':
+    # the table listbox3 has not table header.
+    # first row is table content, too.
+    # +-------------------------------+
+    # |  1    |   2   |   3   |   4   |
+    # |-------+-------+-------+-------|
+    # |       |       |       |       |
+    # +-------+-------+-------+-------+
+    # 
+
+    # 1. Normal Case: ODF table last row is stat line
+    test1.foo_1.setTitle('foo_title_1')
+    foo_form = test1.Foo_view
+    listbox = foo_form.listbox
+    request = self.app.REQUEST 
+    request['here'] = test1
+    listboxline_list = listbox.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    self.assertEqual(len(listboxline_list), 3)
+    self.assertTrue(listboxline_list[0].isTitleLine())
+    self.assertTrue(listboxline_list[1].isDataLine())
+    self.assertTrue(listboxline_list[2].isStatLine())
+    column_list = listboxline_list[0].getColumnPropertyList()
+    self.assertEqual(len(column_list), 4)
+    self.assertTrue(listboxline_list[1].getColumnProperty('id') == "foo_1")
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_1")
+    
+    odf_document = foo_printout.index_html(REQUEST=request)
+    #test_output = open("/tmp/test_02_01_Table.odf", "w")
+    #test_output.write(odf_document)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(content_xml.find("foo_title_1") > 0)
+
+    # 2. Irregular case: listbox columns count smaller than table columns count
+    test1.foo_1.setTitle('foo_title_2')
+    message = listbox.ListBox_setPropertyList(
+      field_list_method = 'objectValues',
+      field_portal_types = 'Foo Line | Foo Line',
+      field_stat_method = 'portal_catalog',
+      field_stat_columns = 'quantity | Foo_statQuantity',
+      field_columns = 'id|ID\ntitle|Title\nquantity|Quantity',)
+    self.failUnless('Set Successfully' in message)
+    self.assertEqual(listbox.get_value('columns'),
+                     [('id', 'ID'), ('title', 'Title'), ('quantity', 'Quantity')])
+    listboxline_list = listbox.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    self.assertEqual(len(listboxline_list), 3)
+    self.assertTrue(listboxline_list[0].isTitleLine())
+    self.assertTrue(listboxline_list[1].isDataLine())
+    self.assertTrue(listboxline_list[2].isStatLine())
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_2")
+    
+    column_list = listboxline_list[0].getColumnPropertyList()
+    self.assertEqual(len(column_list), 3)
+
+    odf_document = foo_printout.index_html(REQUEST=request)
+    #test_output = open("/tmp/test_02_02_Table.odf", "w")
+    #test_output.write(odf_document)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertFalse(content_xml.find("foo_title_1") > 0)
+    self.assertTrue(content_xml.find("foo_title_2") > 0)
+
+    # 3. Irregular case: listbox columns count larger than table columns count
+    test1.foo_1.setTitle('foo_title_3')
+    message = listbox.ListBox_setPropertyList(
+      field_list_method = 'objectValues',
+      field_portal_types = 'Foo Line | Foo Line',
+      field_stat_method = 'portal_catalog',
+      field_stat_columns = 'quantity | Foo_statQuantity',
+      field_columns = 'id|ID\ntitle|Title\nquantity|Quantity\n'
+                      'start_date|Date\nstatus|Status',)
+    self.failUnless('Set Successfully' in message)
+    listboxline_list = listbox.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    self.assertEqual(len(listboxline_list), 3)
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_3")
+    
+    column_list = listboxline_list[0].getColumnPropertyList()
+    self.assertEqual(len(column_list), 5)
+    odf_document = foo_printout.index_html(REQUEST=request)
+    LOG('testFormPrintout', INFO, 'content:%s' % content_xml)
+    #test_output = open("/tmp/test_02_03_Table.odf", "w")
+    #test_output.write(odf_document)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertFalse(content_xml.find("foo_title_2") > 0)
+    self.assertTrue(content_xml.find("foo_title_3") > 0)
+
+    # 4. Irregular case: listbox have not a stat line, but table has a stat line
+    if test1._getOb("foo_2", None) is None:
+      test1.newContent("foo_2", portal_type='Foo Line')
+    get_transaction().commit()
+    self.tic()
+
+    test1.foo_1.setTitle('foo_title_4')
+    message = listbox.ListBox_setPropertyList(
+      field_list_method = 'objectValues',
+      field_portal_types = 'Foo Line | Foo Line',
+      field_stat_method = '',
+      field_stat_columns = 'quantity | Foo_statQuantity',
+      field_columns = 'id|ID\ntitle|Title\nquantity|Quantity\nstart_date|Date',)
+    self.failUnless('Set Successfully' in message)
+    listboxline_list = listbox.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    for line in listboxline_list:
+      self.assertEqual(line.isStatLine(), False)
+    self.assertEqual(len(listboxline_list), 3)
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_4")
+
+    odf_document = foo_printout.index_html(REQUEST=request)
+    #test_output = open("/tmp/test_02_04_Table.odf", "w")
+    #test_output.write(odf_document)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertTrue(odf_document is not None)
+    self.assertFalse(content_xml.find("foo_title_3") > 0)
+    self.assertTrue(content_xml.find("foo_title_4") > 0)
+
+    # 5. Normal case: the listobx of a form and the ODF table are same layout
+    foo_form.manage_renameObject('listbox', 'listbox2', REQUEST=request)
+    listbox2 = foo_form.listbox2
+    test1.foo_1.setTitle('foo_title_5')
+    message = listbox2.ListBox_setPropertyList(
+      field_list_method = 'objectValues',
+      field_portal_types = 'Foo Line | Foo Line',
+      field_stat_method = 'portal_catalog',
+      field_stat_columns = 'quantity | Foo_statQuantity',
+      field_columns = 'id|ID\ntitle|Title\nquantity|Quantity\nstart_date|Date',)
+    self.failUnless('Set Successfully' in message)
+    listboxline_list = listbox2.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    self.assertEqual(len(listboxline_list), 4)
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_5")
+
+    odf_document = foo_printout.index_html(REQUEST=request)
+    #test_output = open("/tmp/test_02_05_Table.odf", "w")
+    #test_output.write(odf_document)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertFalse(content_xml.find("foo_title_4") > 0)
+    self.assertTrue(content_xml.find("foo_title_5") > 0)
+    
+    # put back the field name
+    foo_form.manage_renameObject('listbox2', 'listbox', REQUEST=request)
+
+    # 6. Normal case: ODF table does not have a header
+    foo_form.manage_renameObject('listbox', 'listbox3', REQUEST=request)
+    listbox3 = foo_form.listbox3
+    test1.foo_1.setTitle('foo_title_6')
+    listboxline_list = listbox3.get_value('default', render_format = 'list',
+                                         REQUEST = request)
+    self.assertEqual(len(listboxline_list), 4)
+    self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_6")
+
+    odf_document = foo_printout.index_html(REQUEST=request)
+    #test_output = open("/tmp/test_02_06_Table.odf", "w")
+    #test_output.write(odf_document)
+    self.assertTrue(odf_document is not None)
+    builder = OOoBuilder(odf_document)
+    content_xml = builder.extract("content.xml")
+    self.assertFalse(content_xml.find("foo_title_5") > 0)
+    self.assertTrue(content_xml.find("foo_title_6") > 0)
+
+    # put back the field name
+    foo_form.manage_renameObject('listbox3', 'listbox', REQUEST=request)
+
+  def _test_03_Frame(self, quiet=0, run=run_all_test):
+    """
+    Frame not supported yet
+    """
+    pass
+
+  def _test_04_Iteration(self, quiet=0, run=run_all_test):
+    """
+    Iteration(ReportSection) not supported yet.
+    Probably to support *ReportBox* would be better.
+    """
+    pass
+
+  def _test_05_Styles(self, quiet=0, run=run_all_test):
+    """
+    styles.xml not supported yet
+    """
+    pass
+
+  def _test_06_Meta(self, quiet=0, run=run_all_test):
+    """
+    meta.xml not supported yet
+    """
+    pass

Added: erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odt
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odt?rev=26251&view=auto
==============================================================================
Binary file - no diff available.

Propchange: erp5/trunk/products/ERP5OOo/tests/test_document/Foo_001.odt
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: erp5/trunk/products/ERP5OOo/www/FormPrintout_manageEdit.zpt
URL: http://svn.erp5.org/erp5/trunk/products/ERP5OOo/www/FormPrintout_manageEdit.zpt?rev=26251&view=auto
==============================================================================
--- erp5/trunk/products/ERP5OOo/www/FormPrintout_manageEdit.zpt (added)
+++ erp5/trunk/products/ERP5OOo/www/FormPrintout_manageEdit.zpt [utf8] Thu Apr  2 12:35:29 2009
@@ -1,0 +1,37 @@
+<h1 tal:replace="structure here/manage_page_header">Header</h1>
+<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure here/manage_tabs">Tabs</h2>
+
+<form action="" method="post" tal:attributes="action request/URL1">
+  <input type="hidden" name=":default_method" value="doSettings">
+  <table width="100%" cellspacing="0" cellpadding="2" border="0">
+    <tr>
+      <td class="form-label">Title</td>
+      <td><input name="title" value="" type="text" size="20"
+                tal:attributes="value request/title | here/title | nothing"/></td>
+    </tr>
+    <tr>
+      <td class="form-label">Form Name</td>
+      <td><input name="form_name" value="default_form_name" type="text" size="20"
+                tal:attributes="value request/form_name | here/form_name | nothing"/></td>
+    </tr>
+    <tr>
+      <td class="form-label">Template</td>
+      <td><input name="template" value="default_template" type="text" size="20"
+                tal:attributes="value request/template | here/template | nothing"/></td>
+    </tr>
+
+    <tr>
+      <td align="left" valign="top">
+      <div class="form-element">
+        <em tal:condition="here/wl_isLocked">Locked by WebDAV</em>
+        <input tal:condition="not:here/wl_isLocked"
+              class="form-element" type="submit"
+              name="doSettings:method" value="Save Changes">
+      </div>
+      </td>
+    </tr>
+  </table>
+</form>
+
+<h1 tal:replace="structure here/manage_page_footer">Footer</h1>




More information about the Erp5-report mailing list