OOoTemplate.py 23.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 __future__ import absolute_import
Arnaud Fontaine's avatar
Arnaud Fontaine committed
31
from past.builtins import basestring
32
from mimetypes import guess_extension
33
from OFS.Image import File
34 35 36 37
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
38
from zope.tal.talinterpreter import FasterStringIO
39
from Products.ERP5Type import PropertySheet
Arnaud Fontaine's avatar
Arnaud Fontaine committed
40
from urllib.parse import quote
41
from Products.ERP5Type.Globals import InitializeClass, DTMLFile, get_request
42
from Acquisition import aq_base
43
from AccessControl import ClassSecurityInfo
44
from .OOoUtils import OOoBuilder
45
from zipfile import ZipFile, ZIP_DEFLATED
Arnaud Fontaine's avatar
Arnaud Fontaine committed
46
from io import BytesIO
47 48
import re
import itertools
Arnaud Fontaine's avatar
Arnaud Fontaine committed
49
import six
50 51

try:
52 53 54
  from webdav.Lockable import ResourceLockedError
  from webdav.WriteLockInterface import WriteLockInterface
  SUPPORTS_WEBDAV_LOCKS = 1
55
except ImportError:
56
  SUPPORTS_WEBDAV_LOCKS = 0
57

58
from Products.Formulator.Widget import convert_to_xml_compatible_string
59 60
from lxml import etree
from lxml.etree import Element
61

62 63 64
# Constructors
manage_addOOoTemplate = DTMLFile("dtml/OOoTemplate_add", globals())

65
def addOOoTemplate(self, id, title="", xml_file_id="content.xml", REQUEST=None):
66 67 68 69
  """Add OOo template to folder.

  id     -- the id of the new OOo template to add
  title  -- the title of the OOo to add
70
  xml_file_id -- The Id of edited xml file
71 72 73
  Result -- empty string
  """
  # add actual object
74
  id = self._setObject(id, OOoTemplate(id, title, xml_file_id))
75 76 77 78 79 80
  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
81 82
  add_and_edit(self, id, REQUEST)
  return ''
83 84

def add_and_edit(self, id, REQUEST):
85 86 87 88 89 90 91 92 93 94 95 96 97
  """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')
98

99

100 101
class OOoTemplateStringIO(FasterStringIO):
  def write(self, s):
102 103 104
    return FasterStringIO.write(
        self,
        convert_to_xml_compatible_string(s).encode('utf-8'))
105

106 107 108 109 110 111 112 113 114
from Products.PageTemplates.Expressions import ZopeContext, createZopeEngine

# On recent Zope, 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):
Arnaud Fontaine's avatar
Arnaud Fontaine committed
115
    if isinstance(text, six.binary_type):
116
      # avoid calling the IUnicodeEncodingConflictResolver utility
Arnaud Fontaine's avatar
Arnaud Fontaine committed
117
      return text.decode('utf-8')
118 119 120 121 122 123 124 125
    return ZopeContext._handleText(self, text, expr)

def createOOoZopeEngine():
    e = createZopeEngine()
    e._create_context = OOoContext
    return e

_engine = createOOoZopeEngine()
126

127
class OOoTemplate(ZopePageTemplate):
128 129 130 131 132 133 134 135 136 137 138 139
  """
  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)
140 141

    - add interface for Cache (http/RAM)
142 143 144 145 146 147 148 149 150 151 152
  """
  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.'
153
  _ODF_content_type_root = 'application/vnd.oasis.opendocument.'
154 155 156 157 158 159 160 161 162 163 164 165 166

  # Declarative Security
  security = ClassSecurityInfo()

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

  # Constructors
  constructors =   (manage_addOOoTemplate, addOOoTemplate)

  # Default Attributes
  ooo_stylesheet = 'Base_getODTStyleSheet'
167
  ooo_script_name = None
168
  ooo_xml_file_id = 'content.xml'
169 170 171 172 173 174 175 176 177 178 179 180 181

  # 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')},
      )
    )

182 183 184 185 186 187
  _properties= ZopePageTemplate._properties + (
                                        {'id': 'filename',
                                         'type': 'tales',
                                         'mode': 'w',}, )
  filename = 'object/title_or_id'

188 189 190 191 192
  security.declareProtected('View management screens', 'formSettings')
  formSettings = PageTemplateFile('www/formSettings', globals(),
                                  __name__='formSettings')
  formSettings._owner = None

193 194
  def __init__(self, id, title, xml_file_id='content.xml', *args,**kw):
    ZopePageTemplate.__init__(self, id, title, *args, **kw)
195 196
    # we store the attachments of the uploaded document
    self.OLE_documents_zipstring = None
197
    self.ooo_xml_file_id = xml_file_id
198

199 200 201 202 203 204 205
  # Recent Zope 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
206

207 208 209
  def pt_upload(self, REQUEST, file=''):
    """Replace the document with the text in file."""
    if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
210
      raise ResourceLockedError("File is locked via WebDAV")
211

Arnaud Fontaine's avatar
Arnaud Fontaine committed
212
    if not isinstance(file, basestring):
213
      if not file: raise ValueError('File not specified')
214 215 216 217 218 219 220 221 222 223 224 225 226 227
      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:
Arnaud Fontaine's avatar
Arnaud Fontaine committed
228
        memory_file = BytesIO()
229 230 231 232 233 234 235 236 237 238
        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
239
      file = builder.prepareContentXml(self.ooo_xml_file_id)
240 241
    return ZopePageTemplate.pt_upload(self, REQUEST, file)

242 243 244 245 246 247 248 249 250 251 252 253 254
  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)
255

256
  security.declareProtected('Change Page Templates', 'doSettings')
257
  def doSettings(self, REQUEST, title, xml_file_id, ooo_stylesheet, script_name=None):
258
    """
259
      Change title, xml_file_id and ooo_stylesheet.
260
    """
261
    if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
262
      raise ResourceLockedError("File is locked via WebDAV")
263
    self.ooo_stylesheet = ooo_stylesheet
264
    self.ooo_script_name = script_name
265
    self.ooo_xml_file_id = xml_file_id
266 267 268 269 270 271 272 273 274 275 276
    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)

277
  def renderIncludes(self, here, text, extra_context, request, sub_document=None):
278
    attached_files_dict = {}
279
    arguments_re = re.compile('''(\S+?)\s*=\s*('|")(.*?)\\2\s*''',re.DOTALL)
280 281 282 283
    def getLengthInfos( opts_dict, opts_names ):
      ret = []
      for opt_name in opts_names:
        try:
284
          val = opts_dict.pop(opt_name)
285 286 287 288 289 290 291 292
          if val.endswith('cm'):
            val = val[:-2]
          val = float( val )
        except (ValueError, KeyError):
          val = None
        ret.append(val)
      return ret

293
    def replaceIncludes(path):
294
      # Find the page template based on the path and remove path from dict
295
      document = self._resolvePath(path)
296 297
      document_text = ZopePageTemplate.pt_render(document,
                                                 extra_context=extra_context)
298

299 300
      # Find the type of the embedded document
      document_type = document.content_type
301

302
      # Prepare a subdirectory to store embedded objects
Arnaud Fontaine's avatar
Arnaud Fontaine committed
303
      actual_idx = next(self.document_counter)
304
      dir_name = '%s%d'%(self._OLE_directory_prefix, actual_idx)
305 306 307

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

309 310 311 312 313
      # 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
314
        try:
315
          ooo_stylesheet = ooo_stylesheet()
316 317
        except AttributeError:
          pass
318 319
        temp_builder = OOoBuilder(ooo_stylesheet)
        stylesheet = temp_builder.extract('styles.xml')
320
      else:
321
        stylesheet = None
322

323
      # Start recursion if necessary
324 325
      sub_attached_files_dict = {}
      if 'office:include' in document_text: # small optimisation to avoid recursion if possible
326
        (document_text, sub_attached_files_dict ) = self.renderIncludes(document_text, dir_name, extra_context, request)
327 328 329 330 331

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

333 334 335
      attached_files_dict.update(sub_attached_files_dict)

      # Build the new tag
336 337
      new_path = './%s' % dir_name.split('/')[-1]
      return new_path
338 339

    def replaceIncludesImg(match):
340
      options_dict = { 'text:anchor-type': 'paragraph' }
341
      options_dict.update((x[0], x[2]) for x in arguments_re.findall(match.group(1)))
342 343 344 345 346 347
      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())
348

349 350
      # If this is not a File, build a new file with this content
      if not isinstance(picture, File):
351
        tmp_picture = self.newContent(temp_object=True, portal_type='Image', id='tmp')
352 353 354
        tmp_picture.setData(picture())
        picture = tmp_picture

355
      picture_type = options_dict.pop('type', None)
356

357 358
      picture_data = getattr(aq_base(picture), 'data', None)
      if picture_data is None:
359
        picture_data = picture.Base_download()
360 361 362 363 364 365 366
        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()
367

368 369
      w, h, maxwidth, maxheight = getLengthInfos(options_dict,
                                  ('width', 'height', 'maxwidth', 'maxheight'))
370

371
      aspect_ratio = 1
372
      try: # try image properties
373
        aspect_ratio = float(picture.width) / float(picture.height)
374
      except (TypeError, ZeroDivisionError):
375
        try: # try Image Document API
376 377 378
          height = float(picture.getHeight())
          if height:
            aspect_ratio = float(picture.getWidth()) / height
379
        except AttributeError: # fallback to Photo API
380 381 382
          height = float(picture.height())
          if height:
            aspect_ratio = float(picture.width()) / height
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
      # 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

Arnaud Fontaine's avatar
Arnaud Fontaine committed
398
      actual_idx = next(self.document_counter)
399
      pic_name = 'Pictures/picture%d%s' \
400
                 % (actual_idx, guess_extension(picture_type) or '')
401 402 403 404 405 406 407 408 409 410 411 412

      # 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))
413 414

      if sub_document: # sub-document means sub-directory
415
        pic_name = sub_document + '/' + pic_name
416 417

      attached_files_dict[pic_name] = dict(
418
        document=picture_data,
419
        doc_type=picture_type,
420
      )
421

422 423
      return replacement

424
    xml_doc = etree.XML(text)
425
    for office_include in xml_doc.xpath('//office:include', namespaces=xml_doc.nsmap):
426
      marshal_list = office_include.xpath('./marshal')
427 428
      if marshal_list:
        from xml.marshal.generic import loads
429 430
        arg_dict = loads(etree.tostring(marshal_list[0], encoding='utf-8',
                                        xml_declaration=True, pretty_print=False))
431 432
        extra_context.update(arg_dict)
        request.other.update(arg_dict)
433
      path = office_include.attrib['path']
434
      del(office_include.attrib['path'])
435
      new_path = replaceIncludes(path)
436 437 438 439 440 441
      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)
442 443
    text = re.sub('<\s*office:include_img\s+(.*?)\s*/\s*>(?s)', replaceIncludesImg, text)

444
    return (text, attached_files_dict)
445 446
  # Proxy method to PageTemplate
  def pt_render(self, source=0, extra_context={}):
447 448 449
    if source:
      return ZopePageTemplate.pt_render(self, source=source,
                                         extra_context=extra_context)
450
    # Get request
451
    request = extra_context.get('REQUEST', self.REQUEST)
452 453 454 455
    # 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
456
      raise ValueError('Can not render a template without a parent acquisition context')
457
    # Retrieve master document
458 459
    ooo_document = None
    # If script is setting, call it
Nicolas Delaby's avatar
Nicolas Delaby committed
460
    if self.ooo_script_name:
461 462 463 464
      ooo_script = getattr(here, self.ooo_script_name)
      ooo_document = ooo_script(self.ooo_stylesheet)
    else:
      ooo_document = getattr(here, self.ooo_stylesheet)
465
    format = request.get('format')
466
    try:
467
      # If style is dynamic, call it
468
      if getattr(aq_base(ooo_document), '__call__', None) is not None:
469 470 471
        request.set('format', None)
        ooo_document = ooo_document()
    finally:
Jérome Perrin's avatar
Jérome Perrin committed
472
      request.set('format', format)
473 474 475 476
    # Create a new builder instance
    ooo_builder = OOoBuilder(ooo_document)
    # Pass builder instance as extra_context
    extra_context['ooo_builder'] = ooo_builder
477

478
    # And render page template
479 480
    doc_xml = ZopePageTemplate.pt_render(self, source=source,
                                         extra_context=extra_context)
481
    doc_xml = convert_to_xml_compatible_string(doc_xml).encode('utf-8')
482 483

    # Replace the includes
484 485
    (doc_xml,attachments_dict) = self.renderIncludes(here, doc_xml,
                                                     extra_context, request)
486

487 488 489 490
    try:
      default_styles_text = ooo_builder.extract('styles.xml')
    except AttributeError:
      default_styles_text = None
491

492 493 494
    # Add the associated files
    for dir_name, document_dict in attachments_dict.iteritems():
      # Special case : the document is an OOo one
495 496 497 498 499 500
      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'])
501
        styles_text = default_styles_text
Arnaud Fontaine's avatar
Arnaud Fontaine committed
502
        if 'stylesheet' in document_dict and document_dict['stylesheet']:
503 504
          styles_text = document_dict['stylesheet']
        if styles_text:
505 506
          ooo_builder.addFileEntry(full_path=dir_name + '/styles.xml',
                                   media_type='text/xml', content=styles_text)
507 508
      else: # Generic case
        ooo_builder.addFileEntry(full_path=dir_name,
509 510
                                 media_type=document_dict['doc_type'],
                                 content=document_dict['document'])
511 512

    # Replace content.xml in master openoffice template
513
    ooo_builder.replace(self.ooo_xml_file_id, doc_xml)
514 515 516 517 518 519 520

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

521
    # Convert if necessary
522
    opts = extra_context.get("options", {})
523

524
    # Get batch_mode
525 526
    batch_mode = opts.get('batch_mode', None)

527
    # If the file has embedded OLE documents, restore them
528
    if self.OLE_documents_zipstring:
529
      additional_builder = OOoBuilder(self.OLE_documents_zipstring)
530
      for name in additional_builder.getNameList():
531 532
        if name not in ('META-INF/manifest.xml',):
          # We don't replace manifest
533
          ooo_builder.replace(name, additional_builder.extract(name))
534

535
    # Update the META information
536 537 538
    ooo_builder.updateManifest()

    # Produce final result
539 540 541
    if batch_mode:
      ooo = ooo_builder.render()
    else:
542
      ooo = ooo_builder.render(name=self.title or self.id, source=source)
543

544
    extension = None
545
    mimetype = ooo_builder.getMimeType()
546
    mimetypes_registry = self.getPortalObject().mimetypes_registry
547
    mimetype_object_list = mimetypes_registry.lookup(mimetype)
548 549 550 551 552 553 554 555 556 557 558
    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()
559

560 561
    tmp_ooo = self.newContent(temp_object=True, portal_type='OOo Document',
      id=self.title_or_id())
562
    tmp_ooo.edit(data=ooo,
563
                 filename=filename,
564
                 content_type=mimetype,)
565 566

    format = opts.get('format', request.get('format', None))
567
    if format:
568 569 570 571
      # 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)
572
      tmp_ooo.setBaseContentType(mimetype)
573

574
    if request is not None and not batch_mode and not source:
575 576 577 578
      return tmp_ooo.index_html(REQUEST=request,
                                RESPONSE=request.RESPONSE,
                                format=format)
    return tmp_ooo.convert(format)[1]
579

580 581 582 583 584 585 586 587 588 589 590 591
  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

592 593 594 595 596 597 598 599 600 601 602
  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')

603 604 605 606
InitializeClass(OOoTemplate)

class FSOOoTemplate(FSPageTemplate, OOoTemplate):

607 608
  meta_type = "ERP5 Filesystem OOo Template"
  icon = "www/OOo.png"
609

610 611
  def __call__(self, *args, **kwargs):
    return OOoTemplate.__call__(self, *args, **kwargs)
612 613 614 615

InitializeClass(FSOOoTemplate)

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