OOoTemplate.py 24.5 KB
Newer Older
Nicolas Delaby's avatar
Nicolas Delaby committed
1
# -*- coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#                    Jean-Paul Smets-Solanes <jp@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.
#
##############################################################################

30
from types import StringType
31
from mimetypes import guess_extension
Rafael Monnerat's avatar
Rafael Monnerat committed
32
from zLOG import LOG , INFO
33
from zLOG import PROBLEM
34
from OFS.Image import File
35 36 37 38
from Products.CMFCore.FSPageTemplate import FSPageTemplate
from Products.CMFCore.DirectoryView import registerFileExtension, registerMetaType
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
39 40 41 42
try:
    from TAL.TALInterpreter import FasterStringIO
except ImportError:
    from zope.tal.talinterpreter import FasterStringIO
43 44
from Products.ERP5Type import PropertySheet
from urllib import quote
45 46
from Products.ERP5Type.Globals import InitializeClass, DTMLFile, get_request
from Products.ERP5Type.Globals import DevelopmentMode
47
from Acquisition import aq_base
48 49
from AccessControl import ClassSecurityInfo
from OOoUtils import OOoBuilder
50 51
from zipfile import ZipFile, ZIP_DEFLATED
try:
52
  from cStringIO import StringIO
53
except ImportError:
54
  from StringIO import StringIO
55 56
import re
import itertools
57 58

try:
59 60 61
  from webdav.Lockable import ResourceLockedError
  from webdav.WriteLockInterface import WriteLockInterface
  SUPPORTS_WEBDAV_LOCKS = 1
62
except ImportError:
63
  SUPPORTS_WEBDAV_LOCKS = 0
64

65
from Products.ERP5.Document.Document import ConversionError
66
import Products.ERP5Type.Document
67

68 69
from lxml import etree
from lxml.etree import Element
70

71 72 73
# Constructors
manage_addOOoTemplate = DTMLFile("dtml/OOoTemplate_add", globals())

74
def addOOoTemplate(self, id, title="", xml_file_id="content.xml", REQUEST=None):
75 76 77 78
  """Add OOo template to folder.

  id     -- the id of the new OOo template to add
  title  -- the title of the OOo to add
79
  xml_file_id -- The Id of edited xml file
80 81 82
  Result -- empty string
  """
  # add actual object
83
  id = self._setObject(id, OOoTemplate(id, title, xml_file_id))
84 85 86 87 88 89
  if REQUEST is not None:
    file = REQUEST.form.get('file')
    if file.filename:
      # Get the template in the associated context and upload the file
      getattr(self,id).pt_upload(REQUEST, file)
      # respond to the add_and_edit button if necessary
90 91
  add_and_edit(self, id, REQUEST)
  return ''
92 93

def add_and_edit(self, id, REQUEST):
94 95 96 97 98 99 100 101 102 103 104 105 106
  """Helper method to point to the object's management screen if
  'Add and Edit' button is pressed.
  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')
107

108 109 110 111 112 113
class OOoTemplateStringIO(FasterStringIO):
  def write(self, s):
    if type(s) == unicode:
      s = s.encode('utf-8')
    FasterStringIO.write(self, s)

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
try:
  from Products.PageTemplates.Expressions import ZopeContext, createZopeEngine
except ImportError:
  # BACK: remove when we drop support for Zope 2.8
  _engine = None
else:
  # On Zope 2.12, we need an engine to decode non-unicode-strings for us 
  class OOoContext(ZopeContext):
    """ ZopeContext variant that ALWAYS converts standard strings through utf-8,
    as needed by OpenOffice, ignoring the preferred encodings in the request.
    """
  
    def _handleText(self, text, expr):
      if isinstance(text, str):
        # avoid calling the IUnicodeEncodingConflictResolver utility
        return unicode(text, 'utf-8')
      return ZopeContext._handleText(self, text, expr)
  
  def createOOoZopeEngine():
      e = createZopeEngine()
      e._create_context = OOoContext
      return e

  _engine = createOOoZopeEngine()

139
class OOoTemplate(ZopePageTemplate):
140 141 142 143 144 145 146 147 148 149 150 151
  """
  A page template which is able to embed and OpenOffice
  file (zip archive) and replace content.xml at render time
  with XML dynamically generated through TAL/TALES/METAL expressions

  TODO:
    - find a way to embed TALES in OOo documents in such
      way that editing with OOo does not destroy TAL/TALES

    - add preprocessing option to handle explicit macros in
      OOo in any language. Include debugging options in this case
      (on preprocessed source rather than pure source)
152 153

    - add interface for Cache (http/RAM)
154 155 156 157 158 159 160 161 162 163 164
  """
  meta_type = "ERP5 OOo Template"
  icon = "www/OOo.png"

  # NOTE: 100 is just pure random starting number
  # it won't influence the code at all
  document_counter = itertools.count(100)
  # Every linked OLE document is in a directory starting with 'Obj'
  _OLE_directory_prefix  = 'Obj'
  # every OOo document have a content-type starting like this
  _OOo_content_type_root = 'application/vnd.sun.xml.'
165
  _ODF_content_type_root = 'application/vnd.oasis.opendocument.'
166 167 168 169 170 171 172 173 174 175 176 177 178

  # Declarative Security
  security = ClassSecurityInfo()

  # Declarative properties
  property_sheets = ( PropertySheet.Base
                    , PropertySheet.SimpleItem)

  # Constructors
  constructors =   (manage_addOOoTemplate, addOOoTemplate)

  # Default Attributes
  ooo_stylesheet = 'Base_getODTStyleSheet'
179
  ooo_script_name = None
180
  ooo_xml_file_id = 'content.xml'
181 182 183 184 185 186 187 188 189 190 191 192 193

  # Default content type
  #content_type = 'application/vnd.sun.xml.writer' # Writer type by default
  content_type = 'text/html' # This is the only for now to produce valid XML

  # Management interface
  manage_options =  ( ZopePageTemplate.manage_options +
      (
        {'label':'Stylesheet Setting', 'action':'formSettings',
        'help':('ERPForm', 'pdfStylesheet.txt')},
      )
    )

194 195 196 197 198 199
  _properties= ZopePageTemplate._properties + (
                                        {'id': 'filename',
                                         'type': 'tales',
                                         'mode': 'w',}, )
  filename = 'object/title_or_id'

200 201 202 203 204
  security.declareProtected('View management screens', 'formSettings')
  formSettings = PageTemplateFile('www/formSettings', globals(),
                                  __name__='formSettings')
  formSettings._owner = None

205 206
  def __init__(self, id, title, xml_file_id='content.xml', *args,**kw):
    ZopePageTemplate.__init__(self, id, title, *args, **kw)
207 208
    # we store the attachments of the uploaded document
    self.OLE_documents_zipstring = None
209
    self.ooo_xml_file_id = xml_file_id
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

  if _engine is not None:
    # Zope 2.12 relies on the ZTK implementation of page templates, 
    # passing it a special expression evaluation context that converts strings
    # to unicode in the presence of the proper request headers.
    # Here we do the same, but forcing utf-8 conversion insteado of expecting
    # request headers.
    def pt_getEngine(self):
      return _engine
  else:
    # BACK: Remove when we drop support for Zope 2.8!
    # Every OOoTemplate uses UTF-8 or Unicode, so a special StringIO class
    # must be used, which does not care about response.
    def StringIO(self):
      return OOoTemplateStringIO()
225

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  def pt_upload(self, REQUEST, file=''):
    """Replace the document with the text in file."""
    if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
      raise ResourceLockedError, "File is locked via WebDAV"

    if type(file) is not StringType:
      if not file: raise ValueError, 'File not specified'
      file = file.read()

    if file.startswith("PK") : # FIXME: this condition is probably not enough
      # this is a OOo zip file, extract the content
      builder = OOoBuilder(file)
      attached_files_list = [n for n in builder.getNameList()
        if n.startswith(self._OLE_directory_prefix)
        or n.startswith('Pictures')
        or n == 'META-INF/manifest.xml' ]
      # destroy a possibly pre-existing OLE document set
      if self.OLE_documents_zipstring:
        self.OLE_documents_zipstring = None
      # create a zip archive and store it
      if attached_files_list:
        memory_file = StringIO()
        try:
          zf = ZipFile(memory_file, mode='w', compression=ZIP_DEFLATED)
        except RuntimeError:
          zf = ZipFile(memory_file, mode='w')
        for attached_file in attached_files_list:
            zf.writestr(attached_file, builder.extract(attached_file) )
        zf.close()
        memory_file.seek(0)
        self.OLE_documents_zipstring = memory_file.read()
      self.content_type = builder.getMimeType()
Nicolas Delaby's avatar
Nicolas Delaby committed
258
      file = builder.prepareContentXml(self.ooo_xml_file_id)
259 260
    return ZopePageTemplate.pt_upload(self, REQUEST, file)

261 262 263 264 265 266 267 268 269 270 271 272 273
  if 'pt_edit' not in ZopePageTemplate.__dict__:
    # Override it only for 2.8 !
    # ZopePageTemplate v.2.8 inherate pt_edit from
    # PageTemplate. If method is defined on ZopePageTemplate
    # means we are under 2.12.
    # Delete me when we drop support of 2.8
    security.declareProtected('Change Page Templates', 'pt_edit')
    def pt_edit(self, text, content_type):
      if content_type:
        self.content_type = str(content_type)
      if hasattr(text, 'read'):
        text = text.read()
      self.write(text)
274

275
  security.declareProtected('Change Page Templates', 'doSettings')
276
  def doSettings(self, REQUEST, title, xml_file_id, ooo_stylesheet, script_name=None):
277
    """
278
      Change title, xml_file_id and ooo_stylesheet.
279
    """
280 281 282
    if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
      raise ResourceLockedError, "File is locked via WebDAV"
    self.ooo_stylesheet = ooo_stylesheet
283
    self.ooo_script_name = script_name
284
    self.ooo_xml_file_id = xml_file_id
285 286 287 288 289 290 291 292 293 294 295
    self.pt_setTitle(title)
    #REQUEST.set('text', self.read()) # May not equal 'text'!
    message = "Saved changes."
    if getattr(self, '_v_warnings', None):
      message = ("<strong>Warning:</strong> <i>%s</i>"
                % '<br>'.join(self._v_warnings))
    return self.formSettings(manage_tabs_message=message)

  def _resolvePath(self, path):
    return self.getPortalObject().unrestrictedTraverse(path)

296
  def renderIncludes(self, here, text, extra_context, request, sub_document=None):
297
    attached_files_dict = {}
298
    arguments_re = re.compile('''(\S+?)\s*=\s*('|")(.*?)\\2\s*''',re.DOTALL)
299 300 301 302
    def getLengthInfos( opts_dict, opts_names ):
      ret = []
      for opt_name in opts_names:
        try:
303
          val = opts_dict.pop(opt_name)
304 305 306 307 308 309 310 311
          if val.endswith('cm'):
            val = val[:-2]
          val = float( val )
        except (ValueError, KeyError):
          val = None
        ret.append(val)
      return ret

312
    def replaceIncludes(path):
313
      # Find the page template based on the path and remove path from dict
314
      document = self._resolvePath(path)
315 316
      document_text = ZopePageTemplate.pt_render(document,
                                                 extra_context=extra_context)
317

318 319
      # Find the type of the embedded document
      document_type = document.content_type
320

321
      # Prepare a subdirectory to store embedded objects
322
      actual_idx = self.document_counter.next()
323
      dir_name = '%s%d'%(self._OLE_directory_prefix, actual_idx)
324 325 326

      if sub_document: # sub-document means sub-directory
        dir_name = sub_document + '/' + dir_name
327

328 329 330 331 332
      # Get the stylesheet of the embedded openoffice document
      ooo_stylesheet = document.ooo_stylesheet
      if ooo_stylesheet:
        ooo_stylesheet = getattr(here, ooo_stylesheet)
        # If ooo_stylesheet is dynamic, call it
333
        try:
334
          ooo_stylesheet = ooo_stylesheet()
335 336
        except AttributeError:
          pass
337 338
        temp_builder = OOoBuilder(ooo_stylesheet)
        stylesheet = temp_builder.extract('styles.xml')
339
      else:
340
        stylesheet = None
341

342
      # Start recursion if necessary
343 344
      sub_attached_files_dict = {}
      if 'office:include' in document_text: # small optimisation to avoid recursion if possible
345
        (document_text, sub_attached_files_dict ) = self.renderIncludes(document_text, dir_name, extra_context, request)
346 347 348 349 350

      # Attach content, style and settings if any
      attached_files_dict[dir_name] = dict(document=document_text,
                                           doc_type=document_type,
                                           stylesheet=stylesheet)
351

352 353 354
      attached_files_dict.update(sub_attached_files_dict)

      # Build the new tag
355 356
      new_path = './%s' % dir_name.split('/')[-1]
      return new_path
357 358

    def replaceIncludesImg(match):
359
      options_dict = { 'text:anchor-type': 'paragraph' }
360
      options_dict.update((x[0], x[2]) for x in arguments_re.findall(match.group(1)))
361 362 363 364 365 366
      for old_name, name, default in (('x', 'svg:x', '0cm'),
                                      ('y', 'svg:y', '0cm'),
                                      ('style', 'draw:style-name', 'fr1')):
        options_dict.setdefault(name, options_dict.pop(old_name, default))

      picture = self._resolvePath(options_dict.pop('path').encode())
367

368 369
      # If this is not a File, build a new file with this content
      if not isinstance(picture, File):
370
        tmp_picture = Products.ERP5Type.Document.newTempImage(self, 'tmp')
371 372 373
        tmp_picture.setData(picture())
        picture = tmp_picture

374
      picture_type = options_dict.pop('type', None)
375

376 377
      picture_data = getattr(aq_base(picture), 'data', None)
      if picture_data is None:
378
        picture_data = picture.Base_download()
379 380 381 382 383 384 385
        if picture_type is None:
          picture_type = picture.content_type()
      else:
        # "standard" filetype case (Image or File)
        picture_data = str(picture_data)
        if picture_type is None:
          picture_type = picture.getContentType()
386

387 388
      w, h, maxwidth, maxheight = getLengthInfos(options_dict,
                                  ('width', 'height', 'maxwidth', 'maxheight'))
389

390
      aspect_ratio = 1
391
      try: # try image properties
392
        aspect_ratio = float(picture.width) / float(picture.height)
393
      except (TypeError, ZeroDivisionError):
394
        try: # try ERP5.Document.Image API
395 396 397
          height = float(picture.getHeight())
          if height:
            aspect_ratio = float(picture.getWidth()) / height
398
        except AttributeError: # fallback to Photo API
399 400 401
          height = float(picture.height())
          if height:
            aspect_ratio = float(picture.width()) / height
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
      # fix a default value and correct the aspect
      if h is None:
        if w is None:
          w = 10.0
        h = w / aspect_ratio
      elif w is None:
        w = h * aspect_ratio
      # picture is too large
      if maxwidth and maxwidth < w:
        w = maxwidth
        h = w / aspect_ratio
      if maxheight and maxheight < h:
        h = maxheight
        w = h * aspect_ratio

      actual_idx = self.document_counter.next()
418
      pic_name = 'Pictures/picture%d%s' \
419
                 % (actual_idx, guess_extension(picture_type) or '')
420 421 422 423 424 425 426 427 428 429 430 431

      # XXX: Pictures directory not managed (seems facultative)
      #  <manifest:file-entry manifest:media-type="" manifest:full-path="ObjBFE4F50D/Pictures/"/>
      is_legacy = 'oasis.opendocument' not in self.content_type
      replacement = ('<draw:frame %s>\n<draw:image %s/></draw:frame>',
                     '<draw:image %s %s/>')[is_legacy] % (
        '''draw:name="ERP5Image%d" svg:width="%.3fcm" svg:height="%.3fcm"%s'''
        % (actual_idx, w, h,
           ''.join(' %s="%s"' % opt for opt in options_dict.iteritems())),
        '''xlink:href="%s%s" xlink:type="simple"
           xlink:show="embed" xlink:actuate="onLoad"'''
        % (is_legacy and '#' or '', pic_name))
432 433

      if sub_document: # sub-document means sub-directory
434
        pic_name = sub_document + '/' + pic_name
435 436

      attached_files_dict[pic_name] = dict(
437
        document=picture_data,
438
        doc_type=picture_type,
439
      )
440

441 442 443 444 445 446 447
      if not (self.content_type.endswith('draw') or
              self.content_type.endswith('presentation') or
              self.content_type.endswith('writer') or
              self.content_type.endswith('text')):
        replacement = '<text:p text:style-name="Standard">'+replacement+'</text:p>'
      return replacement

448 449 450
    xml_doc = etree.XML(text)
    for office_include in xml_doc.xpath('//*[name() = "office:include"]'):
      marshal_list = office_include.xpath('./marshal')
451 452
      if marshal_list:
        from xml.marshal.generic import loads
453 454
        arg_dict = loads(etree.tostring(marshal_list[0], encoding='utf-8',
                                        xml_declaration=True, pretty_print=False))
455 456
        extra_context.update(arg_dict)
        request.other.update(arg_dict)
457
      path = office_include.attrib['path']
458
      del(office_include.attrib['path'])
459
      new_path = replaceIncludes(path)
460 461 462 463 464 465
      draw_object = Element('{%s}object' % xml_doc.nsmap.get('draw'))
      draw_object.attrib.update({'{%s}href' % xml_doc.nsmap.get('xlink'): new_path})
      draw_object.attrib.update(dict(office_include.attrib))
      office_include.getparent().replace(office_include, draw_object)
    text = etree.tostring(xml_doc, encoding='utf-8', xml_declaration=True,
                          pretty_print=False)
466 467
    text = re.sub('<\s*office:include_img\s+(.*?)\s*/\s*>(?s)', replaceIncludesImg, text)

468
    return (text, attached_files_dict)
469 470 471
  # Proxy method to PageTemplate
  def pt_render(self, source=0, extra_context={}):
    # Get request
472
    request = extra_context.get('REQUEST', self.REQUEST)
473 474 475 476 477 478
    # Get parent object (the one to render this template on)
    here = getattr(self, 'aq_parent', None)
    if here is None:
      # This is a system error
      raise ValueError, 'Can not render a template without a parent acquisition context'
    # Retrieve master document
479 480
    ooo_document = None
    # If script is setting, call it
Nicolas Delaby's avatar
Nicolas Delaby committed
481
    if self.ooo_script_name:
482 483 484 485
      ooo_script = getattr(here, self.ooo_script_name)
      ooo_document = ooo_script(self.ooo_stylesheet)
    else:
      ooo_document = getattr(here, self.ooo_stylesheet)
486
    format = request.get('format')
487
    try:
488
      # If style is dynamic, call it
489
      if getattr(aq_base(ooo_document), '__call__', None) is not None:
490 491 492
        request.set('format', None)
        ooo_document = ooo_document()
    finally:
Jérome Perrin's avatar
Jérome Perrin committed
493
      request.set('format', format)
494 495 496 497
    # Create a new builder instance
    ooo_builder = OOoBuilder(ooo_document)
    # Pass builder instance as extra_context
    extra_context['ooo_builder'] = ooo_builder
498

499
    # And render page template
500 501
    doc_xml = ZopePageTemplate.pt_render(self, source=source,
                                         extra_context=extra_context)
502 503
    if isinstance(doc_xml, unicode):
      doc_xml = doc_xml.encode('utf-8')
504 505

    # Replace the includes
506 507
    (doc_xml,attachments_dict) = self.renderIncludes(here, doc_xml,
                                                     extra_context, request)
508

509 510 511 512
    try:
      default_styles_text = ooo_builder.extract('styles.xml')
    except AttributeError:
      default_styles_text = None
513

514 515 516
    # Add the associated files
    for dir_name, document_dict in attachments_dict.iteritems():
      # Special case : the document is an OOo one
517 518 519 520 521 522
      if document_dict['doc_type'].startswith(self._OOo_content_type_root) or \
         document_dict['doc_type'].startswith(self._ODF_content_type_root):
        ooo_builder.addFileEntry(full_path=dir_name,
                                 media_type=document_dict['doc_type'])
        ooo_builder.addFileEntry(full_path=dir_name + '/content.xml',
                                 media_type='text/xml', content=document_dict['document'])
523 524 525 526
        styles_text = default_styles_text
        if document_dict.has_key('stylesheet') and document_dict['stylesheet']:
          styles_text = document_dict['stylesheet']
        if styles_text:
527 528
          ooo_builder.addFileEntry(full_path=dir_name + '/styles.xml',
                                   media_type='text/xml', content=styles_text)
529 530
      else: # Generic case
        ooo_builder.addFileEntry(full_path=dir_name,
531 532
                                 media_type=document_dict['doc_type'],
                                 content=document_dict['document'])
533 534 535 536 537 538

    # Debug mode
    if request.get('debug',0):
      return doc_xml

    # Replace content.xml in master openoffice template
539
    ooo_builder.replace(self.ooo_xml_file_id, doc_xml)
540 541 542 543 544 545 546

    # Old templates correction
    try:
      self.OLE_documents_zipstring
    except AttributeError:
      self.OLE_documents_zipstring = None

547
    # Convert if necessary
548
    opts = extra_context.get("options", dict())
549

550
    # Get batch_mode
551 552
    batch_mode = opts.get('batch_mode', None)

553 554 555 556 557 558 559 560 561 562
    # If the file has embedded OLE documents, restore it
    if self.OLE_documents_zipstring:
      additional_builder = OOoBuilder( self.OLE_documents_zipstring )
      for name in additional_builder.getNameList():
        ooo_builder.replace(name, additional_builder.extract(name) )

    # Update the META informations
    ooo_builder.updateManifest()

    # Produce final result
563 564 565
    if batch_mode:
      ooo = ooo_builder.render()
    else:
566
      ooo = ooo_builder.render(name=self.title or self.id, source=source)
567 568 569 570 571 572 573 574 575

    if DevelopmentMode:
      # Validate XML in development mode
      from Products.ERP5OOo.tests.utils import Validator
      err_list = Validator().validate(ooo)
      if err_list:
        LOG('ERP5OOo', PROBLEM,
            'Validation of %s failed:\n%s' % (self.getId(), ''.join(err_list)))

576
    extension = None
577
    mimetype = ooo_builder.getMimeType()
578
    mimetypes_registry = self.getPortalObject().mimetypes_registry
579
    mimetype_object_list = mimetypes_registry.lookup(mimetype)
580 581 582 583 584 585 586 587 588 589 590
    for mimetype_object in mimetype_object_list:
      if mimetype_object.extensions:
        extension = mimetype_object.extensions[0]
        break
      elif mimetype_object.globs:
        extension = mimetype_object.globs.strip('*.')
        break
    if extension:
      filename = '%s.%s' % (self._getFileName(), extension)
    else:
      filename = self._getFileName()
591 592 593 594

    from Products.ERP5Type.Document import newTempOOoDocument
    tmp_ooo = newTempOOoDocument(self, self.title_or_id())
    tmp_ooo.edit(data=ooo,
595
                 filename=filename,
596
                 content_type=mimetype,)
597 598

    format = opts.get('format', request.get('format', None))
599
    if format:
600 601 602 603
      # Performance improvement:
      # We already have OOo format data, so we do not need to call
      # convertToBaseFormat(), but just copy it into base_data property.
      tmp_ooo.setBaseData(ooo)
604
      tmp_ooo.setBaseContentType(mimetype)
605

606
    if request is not None and not batch_mode and not source:
607 608 609 610
      return tmp_ooo.index_html(REQUEST=request,
                                RESPONSE=request.RESPONSE,
                                format=format)
    return tmp_ooo.convert(format)[1]
611

612 613 614 615 616 617 618 619 620 621 622 623
  def om_icons(self):
    """Return a list of icon URLs to be displayed by an ObjectManager"""
    icons = ({'path': 'misc_/ERP5OOo/OOo.png',
              'alt': self.meta_type, 'title': self.meta_type},)
    if not self._v_cooked:
        self._cook()
    if self._v_errors:
        icons = icons + ({'path': 'misc_/PageTemplates/exclamation.gif',
                          'alt': 'Error',
                          'title': 'This template has an error'},)
    return icons

624 625 626 627 628 629 630 631 632 633 634
  def _getFileName(self):
    """Returns the filename used for content-disposition header.
    """
    # The "filename" property has a TALES type, but getProperty for TALES types
    # only works if the context has an ERP5 Site in his acquisition context.
    # If it's not the case, we will not evaluate the TALES, but simply use the
    # template's title or id as filename.
    if getattr(self, 'getPortalObject', None) is None:
      return self.title_or_id()
    return self.getProperty('filename')

635 636 637 638
InitializeClass(OOoTemplate)

class FSOOoTemplate(FSPageTemplate, OOoTemplate):

639 640
  meta_type = "ERP5 Filesystem OOo Template"
  icon = "www/OOo.png"
641

642 643
  def __call__(self, *args, **kwargs):
    return OOoTemplate.__call__(self, *args, **kwargs)
644 645 646 647

InitializeClass(FSOOoTemplate)

registerFileExtension('ooot', FSOOoTemplate)
648
registerMetaType(OOoTemplate.meta_type, FSOOoTemplate)