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

29
from Shared.DC.ZRDB.Connection import Connection as RDBConnection
30
from Globals import Persistent, PersistentMapping
31
from Acquisition import Implicit, aq_base
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32 33
from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
34
from Products.ERP5Type.Base import WorkflowMethod, _aq_reset
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
from Products.ERP5Type.Utils import readLocalDocument, \
                                    writeLocalDocument, \
                                    importLocalDocument, \
                                    removeLocalDocument
from Products.ERP5Type.Utils import readLocalPropertySheet, \
                                    writeLocalPropertySheet, \
                                    importLocalPropertySheet, \
                                    removeLocalPropertySheet
from Products.ERP5Type.Utils import readLocalConstraint, \
                                    writeLocalConstraint, \
                                    importLocalConstraint, \
                                    removeLocalConstraint
from Products.ERP5Type.Utils import readLocalExtension, \
                                    writeLocalExtension, \
                                    removeLocalExtension
from Products.ERP5Type.Utils import readLocalTest, \
                                    writeLocalTest, \
                                    removeLocalTest
53
from Products.ERP5Type import Permissions, PropertySheet
Jean-Paul Smets's avatar
Jean-Paul Smets committed
54
from Products.ERP5Type.XMLObject import XMLObject
55
from Products.ERP5Type.RoleInformation import RoleInformation
56
import fnmatch
57
import re, os
Aurel's avatar
Aurel committed
58
from OFS.Traversable import NotFound
59 60
from OFS import XMLExportImport
from cStringIO import StringIO
Aurel's avatar
Aurel committed
61
from copy import deepcopy
62
from zExceptions import BadRequest
Aurel's avatar
Aurel committed
63 64 65 66
import OFS.XMLExportImport
customImporters={
    XMLExportImport.magic: XMLExportImport.importXML,
    }
Jean-Paul Smets's avatar
Jean-Paul Smets committed
67

68
from zLOG import LOG, WARNING
69
from warnings import warn
Aurel's avatar
Aurel committed
70 71
from OFS.ObjectManager import customImporters
from gzip import GzipFile
72
from xml.dom.minidom import parse
73
from Products.CMFCore.Expression import Expression
Aurel's avatar
Aurel committed
74
import tarfile
75
from urllib import quote, unquote
76
from difflib import unified_diff
77
import posixpath
78
import shutil
79

80 81
# those attributes from CatalogMethodTemplateItem are kept for
# backward compatibility
Aurel's avatar
Aurel committed
82 83
catalog_method_list = ('_is_catalog_list_method_archive',
                       '_is_uncatalog_method_archive',
84 85
                       '_is_clear_method_archive',
                       '_is_filtered_archive',)
86

87 88 89
catalog_method_filter_list = ('_filter_expression_archive',
                              '_filter_expression_instance_archive',
                              '_filter_type_archive',)
90

91 92
INSTALLED_BT_FOR_DIFF = 'installed_bt_for_diff'

93 94 95 96 97 98
def _getCatalog(acquisition_context):
  """
    Return the id of the SQLCatalog which correspond to the current BT.
  """
  catalog_method_id_list = acquisition_context.getTemplateCatalogMethodIdList()
  if len(catalog_method_id_list) == 0:
99 100 101 102
    try:
      return acquisition_context.getPortalObject().portal_catalog.objectIds('SQLCatalog')[0]
    except IndexError:
      return None
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
  catalog_method_id = catalog_method_id_list[0]
  return catalog_method_id.split('/')[0]

def _getCatalogValue(acquisition_context):
  """
    Returns the catalog object which correspond to the ZSQLMethods
    stored/to store in the business template.

    NB: acquisition_context must make possible to reach portal object
        and getTemplateCatalogMethodIdList.
  """
  catalog_id = _getCatalog(acquisition_context)
  if catalog_id is None:
    return None
  try:
    return acquisition_context.getPortalObject().portal_catalog[catalog_id]
  except KeyError:
    return None

122 123 124 125 126
def _recursiveRemoveUid(obj):
  """Recusivly set uid to None, to prevent (un)indexing.
  This is used to prevent unindexing real objects when we delete subobjects on
  a copy of this object.
  """
127 128
  if hasattr(aq_base(obj), 'uid'):
    obj.uid = None
129 130 131
  for subobj in obj.objectValues():
    _recursiveRemoveUid(subobj)

132
def removeAll(entry):
133 134 135
  warn('removeAll is deprecated; use shutil.rmtree instead.',
       DeprecationWarning)
  shutil.rmtree(entry, True)
Aurel's avatar
Aurel committed
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
def getChainByType(context):
  """
  This is used in order to construct the full list
  of mapping between type and list of workflow associated
  This is only useful in order to use
  portal_workflow.manage_changeWorkflows
  """
  pw = context.portal_workflow
  cbt = pw._chains_by_type
  ti = pw._listTypeInfo()
  types_info = []
  for t in ti:
    id = t.getId()
    title = t.Title()
    if title == id:
      title = None
    if cbt is not None and cbt.has_key(id):
      chain = ', '.join(cbt[id])
    else:
      chain = '(Default)'
    types_info.append({'id': id,
                      'title': title,
                      'chain': chain})
  new_dict = {}
  for item in types_info:
    new_dict['chain_%s' % item['id']] = item['chain']
  default_chain=', '.join(pw._default_chain)
  return (default_chain, new_dict)

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
def fixZSQLMethod(portal, method):
  """Make sure the ZSQLMethod uses a valid connection.
  """
  if not isinstance(getattr(portal, method.connection_id, None),
                      RDBConnection):
    # if not valid, we assign to the first valid connection found
    sql_connection_list = portal.objectIds(
                          spec=('Z MySQL Database Connection',))
    if (method.connection_id not in sql_connection_list) and \
       (len(sql_connection_list) != 0):
      LOG('BusinessTemplate', WARNING,
          'connection_id for Z SQL Method %s is invalid, using %s' % (
                    method.getId(), sql_connection_list[0]))
      method.connection_id = sql_connection_list[0]

Aurel's avatar
Aurel committed
181 182 183
class BusinessTemplateArchive:
  """
    This is the base class for all Business Template archives
184
  """
Aurel's avatar
Aurel committed
185 186 187 188 189 190 191 192 193 194 195 196

  def __init__(self, creation=0, importing=0, file=None, path=None, **kw):
    if creation:
      self._initCreation(path=path, **kw)
    elif importing:
      self._initImport(file=file, path=path, **kw)

  def addFolder(self, **kw):
    pass

  def addObject(self, *kw):
    pass
197

Aurel's avatar
Aurel committed
198 199 200 201 202
  def finishCreation(self, **kw):
    pass

class BusinessTemplateFolder(BusinessTemplateArchive):
  """
Christophe Dumez's avatar
Christophe Dumez committed
203
    Class archiving business template into a folder tree
204
  """
Aurel's avatar
Aurel committed
205 206 207 208 209 210
  def _initCreation(self, path):
    self.path = path
    try:
      os.makedirs(self.path)
    except OSError:
      # folder already exists, remove it
211
      shutil.rmtree(self.path)
Aurel's avatar
Aurel committed
212 213 214
      os.makedirs(self.path)

  def addFolder(self, name=''):
Jérome Perrin's avatar
Jérome Perrin committed
215
    if name != '':
216
      name = os.path.normpath(name)
Aurel's avatar
Aurel committed
217
      path = os.path.join(self.path, name)
218
      if not os.path.exists(path):
Aurel's avatar
Aurel committed
219 220 221
        os.makedirs(path)
      return path

222
  def addObject(self, obj, name, path=None, ext='.xml'):
223 224 225
    name = name.replace('\\', '/')
    name = quote(name)
    name = os.path.normpath(name)
Aurel's avatar
Aurel committed
226 227 228
    if path is None:
      object_path = os.path.join(self.path, name)
    else:
229
      if '%' not in path:
230 231 232 233
        tail, path = os.path.splitdrive(path)
        path = path.replace('\\', '/')
        path = tail + quote(path)
      path = os.path.normpath(path)
Aurel's avatar
Aurel committed
234
      object_path = os.path.join(path, name)
235
    f = open(object_path+ext, 'wb')
236
    f.write(str(obj))
Aurel's avatar
Aurel committed
237 238 239
    f.close()

  def _initImport(self, file=None, path=None, **kw):
240
    # Normalize the paths to eliminate the effect of double-slashes.
241 242 243 244 245 246 247 248
    root_path_len = len(os.path.normpath(path)) + len(os.sep)
    self.root_path_len = root_path_len
    d = {}
    for f in file:
      f = os.path.normpath(f)
      klass = f[root_path_len:].split(os.sep, 1)[0]
      d.setdefault(klass, []).append(f)
    self.file_list_dict = d
Aurel's avatar
Aurel committed
249

250
  def importFiles(self, item, **kw):
Aurel's avatar
Aurel committed
251 252 253
    """
      Import file from a local folder
    """
254
    class_name = item.__class__.__name__
255 256 257 258 259 260 261
    root_path_len = self.root_path_len
    prefix_len = root_path_len + len(class_name) + len(os.sep)
    for file_path in self.file_list_dict.get(class_name, ()):
      if os.path.isfile(file_path):
        file = open(file_path, 'rb')
        try:
          file_name = file_path[prefix_len:]
262
          if '%' in file_name:
263
            file_name = unquote(file_name)
264
          item._importFile(file_name, file)
265
        finally:
266
          file.close()
267

Aurel's avatar
Aurel committed
268 269 270 271 272 273
class BusinessTemplateTarball(BusinessTemplateArchive):
  """
    Class archiving businnes template into a tarball file
  """

  def _initCreation(self, path):
274
    # make tmp dir, must use stringIO instead
Aurel's avatar
Aurel committed
275 276 277 278 279
    self.path = path
    try:
      os.makedirs(self.path)
    except OSError:
      # folder already exists, remove it
280
      shutil.rmtree(self.path)
Aurel's avatar
Aurel committed
281 282 283 284 285 286
      os.makedirs(self.path)
    # init tarfile obj
    self.fobj = StringIO()
    self.tar = tarfile.open('', 'w:gz', self.fobj)

  def addFolder(self, name=''):
287
    name = os.path.normpath(name)
Aurel's avatar
Aurel committed
288
    if not os.path.exists(name):
Aurel's avatar
Aurel committed
289 290
      os.makedirs(name)

291
  def addObject(self, obj, name, path=None, ext='.xml'):
292 293 294
    name = name.replace('\\', '/')
    name = quote(name)
    name = os.path.normpath(name)
Aurel's avatar
Aurel committed
295 296 297
    if path is None:
      object_path = os.path.join(self.path, name)
    else:
298
      if '%' not in path:
299 300 301 302
        tail, path = os.path.splitdrive(path)
        path = path.replace('\\', '/')
        path = tail + quote(path)
      path = os.path.normpath(path)
Aurel's avatar
Aurel committed
303
      object_path = os.path.join(path, name)
304
    f = open(object_path+ext, 'wb')
305
    f.write(str(obj))
Aurel's avatar
Aurel committed
306 307 308 309 310
    f.close()

  def finishCreation(self):
    self.tar.add(self.path)
    self.tar.close()
311
    shutil.rmtree(self.path)
Aurel's avatar
Aurel committed
312 313 314 315 316
    return self.fobj

  def _initImport(self, file=None, **kw):
    self.f = file

317
  def importFiles(self, item, **kw):
Aurel's avatar
Aurel committed
318 319
    """
      Import all file from the archive to the site
320
    """
321
    class_name = item.__class__.__name__
Aurel's avatar
Aurel committed
322 323 324 325 326
    self.f.seek(0)
    data = GzipFile(fileobj=self.f).read()
    io = StringIO(data)
    tar = tarfile.TarFile(fileobj=io)
    for info in tar.getmembers():
Yoshinori Okuji's avatar
Yoshinori Okuji committed
327 328
      if 'CVS' in info.name.split('/'):
        continue
Yoshinori Okuji's avatar
Yoshinori Okuji committed
329 330
      if '.svn' in info.name.split('/'):
        continue
331
      if class_name in info.name.split('/'):
Aurel's avatar
Aurel committed
332 333
        if info.isreg():
          file = tar.extractfile(info)
334 335
          tar_file_name = info.name.startswith('./') and info.name[2:] or \
              info.name
336
          folders = tar_file_name.split('/')
337
          file_name = ('/').join(folders[2:])
338
          if '%' in file_name:
339
            file_name = unquote(file_name)
340
          item._importFile(file_name, file)
Aurel's avatar
Aurel committed
341 342 343
          file.close()
    tar.close()
    io.close()
Jean-Paul Smets's avatar
Jean-Paul Smets committed
344

345
class TemplateConditionError(Exception): pass
346
class TemplateConflictError(Exception): pass
347
class BusinessTemplateMissingDependency(Exception): pass
348

349
class BaseTemplateItem(Implicit, Persistent):
350
  """
351
    This class is the base class for all template items.
352
  """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
353

354
  def __init__(self, id_list, **kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
355
    self.__dict__.update(kw)
356
    self._archive = PersistentMapping()
Aurel's avatar
Aurel committed
357
    self._objects = PersistentMapping()
358
    for id in id_list:
359 360
      if id is not None and id != '':
        self._archive[id] = None
361 362 363 364

  def build(self, context, **kw):
    pass

365
  def preinstall(self, context, installed_bt, **kw):
Vincent Pelletier's avatar
Vincent Pelletier committed
366 367 368 369 370 371 372 373
    """
      Build a list of added/removed/changed files between the BusinessTemplate
      being installed (self) and the installed one (installed_bt).
      Note : we compare files between BTs, *not* between the installed BT and
      the objects in the DataFS.

      XXX: -12 used here is -len('TemplateItem')
    """
Yoshinori Okuji's avatar
Yoshinori Okuji committed
374 375
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
376
      new_keys = self._objects.keys()
377
      for path in new_keys:
378
        if installed_bt._objects.has_key(path):
Vincent Pelletier's avatar
Vincent Pelletier committed
379
          # compare objects to see it there are changes
380 381
          new_obj_xml = self.generateXml(path=path)
          old_obj_xml = installed_bt.generateXml(path=path)
382 383
          if new_obj_xml != old_obj_xml:
            modified_object_list.update({path : ['Modified', self.__class__.__name__[:-12]]})
Vincent Pelletier's avatar
Vincent Pelletier committed
384
          # else, compared versions are identical, don't overwrite the old one
385 386
        else: # new object
          modified_object_list.update({path : ['New', self.__class__.__name__[:-12]]})
Vincent Pelletier's avatar
Vincent Pelletier committed
387
      # list removed objects
388 389 390 391 392 393 394
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

  def install(self, context, trashbin, **kw):
395
    pass
396 397 398

  def uninstall(self, context, **kw):
    pass
399

400
  def remove(self, context, **kw):
Vincent Pelletier's avatar
Vincent Pelletier committed
401 402 403 404 405 406
    """
      If 'remove' is chosen on an object containing subobjects, all the
      subobjects will be removed too, even if 'backup' or 'keep' was chosen for
      the subobjects.
      Likewise, for 'save_and_remove' : subobjects will get saved too.
    """
407 408 409
    remove_dict = kw.get('remove_object_dict', {})
    keys = self._objects.keys()
    keys.sort()
410
    keys.reverse()
411 412 413 414 415 416 417 418 419 420 421
    # if you choose remove, the object and all its subobjects will be removed
    # even if you choose backup or keep for subobjects
    # it is same behaviour for backup_and_remove, all we be save
    for path in keys:
      if remove_dict.has_key(path):
        action = remove_dict[path]
        if action == 'save_and_remove':
          # like trash
          self.uninstall(context, trash=1, object_path=path, **kw)
        elif action == 'remove':
          self.uninstall(context, trash=0, object_path=path, **kw)
422

423

424 425 426 427
  def trash(self, context, new_item, **kw):
    # trash is quite similar to uninstall.
    return self.uninstall(context, new_item=new_item, trash=1, **kw)

Aurel's avatar
Aurel committed
428
  def export(self, context, bta, **kw):
429
    pass
Aurel's avatar
Aurel committed
430

431 432
  def getKeys(self):
    return self._objects.keys()
433

Aurel's avatar
Aurel committed
434
  def importFile(self, bta, **kw):
435
    bta.importFiles(item=self)
436

437 438 439
  def removeProperties(self, obj):
    """
    Remove unneeded properties for export
440
    """
441 442
    _marker = []
    base_obj = aq_base(obj)
443

444
    if getattr(base_obj, '_dav_writelocks', _marker) is not _marker:
445
      del aq_base(obj)._dav_writelocks
446
    if getattr(base_obj, '__ac_local_roles__', _marker) is not _marker:
447 448
      # remove local roles
      obj.__ac_local_roles__ = None
449
    if getattr(base_obj, '_owner', _marker) is not _marker:
450
      obj._owner = None
451
    if getattr(base_obj, 'uid', _marker) is not _marker:
452
      obj.uid = None
453
    if getattr(base_obj, '_filepath', _marker) is not _marker:
454
      obj._filepath = None
455 456 457
    if getattr(base_obj, 'workflow_history', _marker) is not _marker:
      if getattr(base_obj.__class__, 'workflow_history', _marker) \
          is not _marker:
458 459 460
        obj.workflow_history = None
      else:
        del obj.workflow_history
461 462
    if getattr(base_obj, 'meta_type', None) == 'Script (Python)':
      if getattr(base_obj, '_code', _marker) is not _marker:
463
        obj._code = None
464
      if getattr(base_obj, 'Python_magic', _marker) is not _marker:
465
        obj.Python_magic = None
466
    elif getattr(base_obj, 'meta_type', None) == 'ERP5 PDF Form' :
467 468
      if not obj.getProperty('business_template_include_content', 1) :
        obj.deletePdfContent()
469 470
    return obj

471 472 473
class ObjectTemplateItem(BaseTemplateItem):
  """
    This class is used for generic objects and as a subclass.
474
  """
475

476 477 478 479
  def __init__(self, id_list, tool_id=None, **kw):
    BaseTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
    if tool_id is not None:
      id_list = self._archive.keys()
480
      self._archive.clear()
481 482 483
      for id in id_list :
        if id != '':
          self._archive["%s/%s" % (tool_id, id)] = None
484

485
  def export(self, context, bta, **kw):
Vincent Pelletier's avatar
Vincent Pelletier committed
486 487 488 489
    """
      Export the business template : fill the BusinessTemplateArchive with
      objects exported as XML, hierarchicaly organised.
    """
490 491 492
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
Vincent Pelletier's avatar
Vincent Pelletier committed
493
    for key, obj in self._objects.iteritems():
494
      # create folder and subfolders
495
      folders, id = posixpath.split(key)
Aurel's avatar
Aurel committed
496 497 498
      encode_folders = []
      for folder in folders.split('/'):
        if '%' not in folder:
499
          encode_folders.append(quote(folder))
Aurel's avatar
Aurel committed
500 501 502
        else:
          encode_folders.append(folder)
      path = os.path.join(root_path, (os.sep).join(encode_folders))
503 504 505
      bta.addFolder(name=path)
      # export object in xml
      f=StringIO()
506 507
      XMLExportImport.exportXML(obj._p_jar, obj._p_oid, f)
      bta.addObject(obj=f.getvalue(), name=id, path=path)
508

Aurel's avatar
Aurel committed
509
  def build_sub_objects(self, context, id_list, url, **kw):
Vincent Pelletier's avatar
Vincent Pelletier committed
510
    # XXX duplicates code from build
Aurel's avatar
Aurel committed
511 512 513
    p = context.getPortalObject()
    sub_list = {}
    for id in id_list:
514
      relative_url = '/'.join([url,id])
515 516
      obj = p.unrestrictedTraverse(relative_url)
      obj = obj._getCopy(context)
517
      obj = self.removeProperties(obj)
Vincent Pelletier's avatar
Vincent Pelletier committed
518 519 520
      id_list = obj.objectIds() # FIXME duplicated variable name
      if hasattr(aq_base(obj), 'groups'): # XXX should check metatype instead
        # we must keep groups because they are deleted along with subobjects
521
        groups = deepcopy(obj.groups)
522
      if id_list:
Aurel's avatar
Aurel committed
523
        self.build_sub_objects(context, id_list, relative_url)
524
        for id_ in list(id_list):
525
          obj._delObject(id_)
526
      if hasattr(aq_base(obj), 'groups'):
527 528 529
        obj.groups = groups
      self._objects[relative_url] = obj
      obj.wl_clearLocks()
Aurel's avatar
Aurel committed
530 531
    return sub_list

532 533 534 535
  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
    for relative_url in self._archive.keys():
536 537 538 539
      try:
        obj = p.unrestrictedTraverse(relative_url)
      except ValueError:
        raise ValueError, "Can not access to %s" % relative_url
540 541 542 543
      try:
        obj = obj._getCopy(context)
      except AttributeError:
        raise AttributeError, "Could not find object '%s' during business template processing." % relative_url
544
      _recursiveRemoveUid(obj)
545
      obj = self.removeProperties(obj)
546
      id_list = obj.objectIds()
Vincent Pelletier's avatar
Vincent Pelletier committed
547 548
      if hasattr(aq_base(obj), 'groups'): # XXX should check metatype instead
        # we must keep groups because they are deleted along with subobjects
549
        groups = deepcopy(obj.groups)
Aurel's avatar
Aurel committed
550 551
      if len(id_list) > 0:
        self.build_sub_objects(context, id_list, relative_url)
552
        for id_ in list(id_list):
553
          obj._delObject(id_)
554
      if hasattr(aq_base(obj), 'groups'):
555 556 557
        obj.groups = groups
      self._objects[relative_url] = obj
      obj.wl_clearLocks()
558

559 560 561 562
  def _compileXML(self, file):
      name, ext = os.path.splitext(file.name)
      compiled_file = name + '.zexp'
      if not os.path.exists(compiled_file) or os.path.getmtime(file.name) > os.path.getmtime(compiled_file):
563
          LOG('Business Template', 0, 'Compiling %s to %s...' % (file.name, compiled_file))
564
          try:
565
              from Shared.DC.xml import ppml
566
              from OFS.XMLExportImport import start_zopedata, save_record, save_zopedata
567
              import pyexpat
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
              outfile=open(compiled_file, 'wb')
              try:
                  data=file.read()
                  F=ppml.xmlPickler()
                  F.end_handlers['record'] = save_record
                  F.end_handlers['ZopeData'] = save_zopedata
                  F.start_handlers['ZopeData'] = start_zopedata
                  F.binary=1
                  F.file=outfile
                  p=pyexpat.ParserCreate()
                  p.CharacterDataHandler=F.handle_data
                  p.StartElementHandler=F.unknown_starttag
                  p.EndElementHandler=F.unknown_endtag
                  r=p.Parse(data)
              finally:
                  outfile.close()
          except:
              if os.path.exists(compiled_file):
                  os.remove(compiled_file)
              raise
      return open(compiled_file)

590
  def _importFile(self, file_name, file_obj):
591
    # import xml file
592
    if not file_name.endswith('.xml'):
593 594
      if not file_name.endswith('.zexp'):
        LOG('Business Template', 0, 'Skipping file "%s"' % (file_name, ))
595
      return
596 597 598 599 600
    obj = self
    connection = None
    while connection is None:
      obj=obj.aq_parent
      connection=obj._p_jar
601
    __traceback_info__ = 'Importing %s' % file_name
602 603 604 605 606 607 608
    # The pre-compilation hack is disabled, because the design is not
    # nice. Do not enable it without yo's approval.
    if 0:
      if isinstance(file_obj, file):
        obj = connection.importFile(self._compileXML(file_obj))
      else:
        obj = connection.importFile(file_obj, customImporters=customImporters)
609 610
    else:
      obj = connection.importFile(file_obj, customImporters=customImporters)
611
    self.removeProperties(obj)
612 613
    self._objects[file_name[:-4]] = obj

614
  def preinstall(self, context, installed_bt, **kw):
Vincent Pelletier's avatar
Vincent Pelletier committed
615
    #XXX -12 used here is -len('TemplateItem')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
616 617
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
618 619 620 621 622 623 624
      portal = context.getPortalObject()
      new_keys = self._objects.keys()
      for path in new_keys:
        if installed_bt._objects.has_key(path):
          # compare object to see it there is changes
          new_object = self._objects[path]
          old_object = installed_bt._objects[path]
625
          old_object = self.removeProperties(old_object)
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
          new_io = StringIO()
          old_io = StringIO()
          OFS.XMLExportImport.exportXML(new_object._p_jar, new_object._p_oid, new_io)
          OFS.XMLExportImport.exportXML(old_object._p_jar, old_object._p_oid, old_io)
          new_obj_xml = new_io.getvalue()
          old_obj_xml = old_io.getvalue()
          new_io.close()
          old_io.close()
          if new_obj_xml != old_obj_xml:
            modified_object_list.update({path : ['Modified', self.__class__.__name__[:-12]]})
        else: # new object
          modified_object_list.update({path : ['New', self.__class__.__name__[:-12]]})
      # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

645
  def _backupObject(self, action, trashbin, container_path, object_id, **kw):
646 647 648
    """
      Backup the object in portal trash if necessery and return its subobjects
    """
649
    subobjects_dict = {}
650
    if trashbin is None: # must return subobjects
651 652 653 654 655 656
      object_path = container_path + [object_id]
      obj = self.unrestrictedTraverse(object_path)
      for subobject_id in list(obj.objectIds()):
        subobject_path = object_path + [subobject_id]
        subobject = self.unrestrictedTraverse(subobject_path)
        subobject_copy = subobject._p_jar.exportFile(subobject._p_oid)
657
        subobjects_dict[subobject_id] = subobject_copy
658
      return subobjects_dict
659 660
    # XXX btsave is for backward compatibility
    if action == 'backup' or action == 'btsave':
661
      subobjects_dict = self.portal_trash.backupObject(trashbin, container_path, object_id, save=1, **kw)
662
    elif action == 'install':
663
      subobjects_dict = self.portal_trash.backupObject(trashbin, container_path, object_id, save=0, **kw)
664
    return subobjects_dict
665

666 667 668
  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
669
    if context.getTemplateFormatVersion() == 1:
670
      groups = {}
671
      old_groups = {}
672 673 674 675 676
      portal = context.getPortalObject()
      # sort to add objects before their subobjects
      keys = self._objects.keys()
      keys.sort()
      for path in keys:
677 678
        if update_dict.has_key(path) or force:
          # get action for the oject
Yoshinori Okuji's avatar
Yoshinori Okuji committed
679
          action = 'backup'
680 681 682 683 684
          if not force:
            action = update_dict[path]
            if action == 'nothing':
              continue
          # get subobjects in path
685 686 687
          path_list = path.split('/')
          container_path = path_list[:-1]
          object_id = path_list[-1]
688 689 690 691
          try:
            container = portal.unrestrictedTraverse(container_path)
          except KeyError:
            # parent object can be set to nothing, in this case just go on
692
            container_url = '/'.join(container_path)
693 694 695 696
            if update_dict.get(container_url) == 'nothing':
              continue
            # If container's container is portal_catalog,
            # then automatically create the container.
697
            elif len(container_path) > 1 and container_path[-2] == 'portal_catalog':
698 699
              # The id match, but better double check with the meta type
              # while avoiding the impact of systematic check
700
              container_container = portal.unrestrictedTraverse(container_path[:-1])
701 702 703 704 705
              if container_container.meta_type == 'ERP5 Catalog':
                container_container.manage_addProduct['ZSQLCatalog'].manage_addSQLCatalog(id=container_path[-1], title='')
                if len(container_container.objectIds()) == 1:
                  container_container.default_sql_catalog_id = container_path[-1]
                container = portal.unrestrictedTraverse(container_path)
706 707
            else:
              raise
708 709
          subobjects_dict = {}
          # Object already exists
710 711 712
          old_obj = container._getOb(object_id, None)
          if old_obj is not None:
            if getattr(aq_base(old_obj), 'groups', None) is not None:
713 714 715 716 717
              # we must keep original order groups
              # from old form in case we keep some
              # old widget, thus we can readd them in
              # the right order group
              old_groups[path] = deepcopy(old_obj.groups)
718
            subobjects_dict = self._backupObject(action, trashbin,
719
                                                 container_path, object_id)
720 721
            container.manage_delObjects([object_id])
          # install object
722
          obj = self._objects[path]
723 724 725
          if getattr(obj, 'meta_type', None) == 'Script (Python)':
            if getattr(obj, '_code') is None:
              obj._compile()
726
          if getattr(aq_base(obj), 'groups', None) is not None:
727
            # we must keep original order groups
728
            # because they change when we add subobjects
729
            groups[path] = deepcopy(obj.groups)
730
          # copy the object
731
          obj = obj._getCopy(container)
732 733 734 735 736
          try:
            container._setObject(object_id, obj)
          except AttributeError:
            LOG("BT, install", 0, object_id)
            raise
737
          obj = container._getOb(object_id)
738 739
          # mark a business template installation so in 'PortalType_afterClone' scripts
          # we can implement logical for reseting or not attributes (i.e reference).
740
          self.REQUEST.set('is_business_template_installation', 1)
741 742
          obj.manage_afterClone(obj)
          obj.wl_clearLocks()
743
          # if portal types upgrade, set backup properties
744 745
          if getattr(obj, 'meta_type', None) == 'ERP5 Type Information' and \
              len(subobjects_dict) > 0:
746
            setattr(obj, 'allowed_content_types',
747
                    subobjects_dict['allowed_content_type_list'] or [])
748
            setattr(obj, 'hidden_content_type_list',
749
                    subobjects_dict['hidden_content_type_list'] or [])
750
            setattr(obj, 'property_sheet_list',
751
                    subobjects_dict['property_sheet_list'] or [])
752
            setattr(obj, 'base_category_list',
753
                    subobjects_dict['base_category_list'] or [])
754 755 756 757
            setattr(obj, '_roles', subobjects_dict['roles_list'] or [])
            # set actions
            action_list = subobjects_dict['action_list']
            for action in action_list:
758 759 760
              action_text = action.action
              if isinstance(action_text, Expression):
                action_text = action_text.text
761 762
              obj.addAction(id = action.id
                            , name = action.title
763
                            , action = action_text
764 765 766 767 768 769 770 771 772 773 774
                            , condition = action.getCondition()
                            , permission = action.permissions
                            , category = action.category
                            , visible = action.visible
                            , icon = getattr(action, 'icon', None) and action.icon.text or ''
                            , priority = action.priority
                            )
            # set workflow chain
            wf_chain = subobjects_dict['workflow_chain']
            chain_dict = getChainByType(context)[1]
            default_chain = ''
775
            chain_dict['chain_%s' % (object_id)] = wf_chain
776
            context.portal_workflow.manage_changeWorkflows(default_chain, props=chain_dict)
777
          # import sub objects if there is
778
          elif len(subobjects_dict) > 0:
779
            # get a jar
780 781
            connection = obj._p_jar
            o = obj
782
            while connection is None:
783 784
              o = o.aq_parent
              connection = o._p_jar
785
            # import subobjects
786
            for subobject_id, subobject_data in subobjects_dict.iteritems():
787 788 789 790 791 792 793 794 795 796 797
              try:
                if obj._getOb(subobject_id, None) is None:
                  subobject_data.seek(0)
                  subobject = connection.importFile(subobject_data)
                  obj._setObject(subobject_id, subobject)
              except AttributeError:
                # XXX this may happen when an object which can contain
                # sub-objects (e.g. ERP5 Form) has been replaced with
                # an object which cannot (e.g. External Method).
                LOG('BusinessTemplate', WARNING,
                    'could not restore %r in %r' % (subobject_id, obj))
798
          if obj.meta_type in ('Z SQL Method',):
799
            fixZSQLMethod(portal, obj)
800
      # now put original order group
801 802
      # we remove object not added in forms
      # we put old objects we have kept
803
      for path, new_groups_dict in groups.iteritems():
804 805 806 807 808 809 810 811 812
        if not old_groups.has_key(path):
          # installation of a new form
          obj = portal.unrestrictedTraverse(path)
          obj.groups = new_groups_dict
        else:
          # upgrade of a form
          old_groups_dict = old_groups[path]
          obj = portal.unrestrictedTraverse(path)
          # first check that all widgets are in new order
813
          # excetp the one that had to be removed
814 815 816 817
          widget_id_list = obj.objectIds()
          for widget_id in widget_id_list:
            widget_path = path+'/'+widget_id
            if update_dict.has_key(widget_path) and update_dict[widget_path] in ('remove', 'save_and_remove'):
818 819
              continue
            widget_in_form = 0
820 821
            for group_id, group_value_list in new_groups_dict.iteritems():
              if widget_id in group_value_list:
822 823 824 825 826 827
                widget_in_form = 1
                break
            # if not, add it in the same groups
            # defined on the former form
            previous_group_id = None
            if not widget_in_form:
828
              for old_group_id, old_group_values in old_groups_dict.iteritems():
829 830 831 832 833 834 835 836 837 838 839
                if widget_id in old_group_values:
                  previous_group_id = old_group_id
              # if we find same group in new one, add widget to it
              if previous_group_id is not None and new_groups_dict.has_key(previous_group_id):
                new_groups_dict[previous_group_id].append(widget_id)
              # otherwise use a specific group
              else:
                if new_groups_dict.has_key('not_assigned'):
                  new_groups_dict['not_assigned'].append(widget_id)
                else:
                  new_groups_dict['not_assigned'] = [widget_id,]
840
                  obj.group_list = list(obj.group_list) + ['not_assigned']
841
          # second check all widget_id in order are in form
842 843
          for group_id, group_value_list in new_groups_dict.iteritems():
            for widget_id in tuple(group_value_list):
844 845 846
              if widget_id not in widget_id_list:
                # if we don't find the widget id in the form
                # remove it fro the group
847
                group_value_list.remove(widget_id)
848
          # now set new group object
849
          obj.groups = new_groups_dict
Aurel's avatar
Aurel committed
850
    else:
851 852
      # for old business template format
      BaseTemplateItem.install(self, context, trashbin, **kw)
Aurel's avatar
Aurel committed
853
      portal = context.getPortalObject()
854
      for relative_url in self._archive.keys():
855
        obj = self._archive[relative_url]
Aurel's avatar
Aurel committed
856 857 858 859
        container_path = relative_url.split('/')[0:-1]
        object_id = relative_url.split('/')[-1]
        container = portal.unrestrictedTraverse(container_path)
        container_ids = container.objectIds()
860
        if object_id in container_ids:    # Object already exists
861
          self._backupObject('backup', trashbin, container_path, object_id)
862
          container.manage_delObjects([object_id])
Aurel's avatar
Aurel committed
863
        # Set a hard link
864 865 866 867 868 869
        obj = obj._getCopy(container)
        container._setObject(object_id, obj)
        obj = container._getOb(object_id)
        obj.manage_afterClone(obj)
        obj.wl_clearLocks()
        if obj.meta_type in ('Z SQL Method',):
870
          fixZSQLMethod(portal, obj)
871 872 873

  def uninstall(self, context, **kw):
    portal = context.getPortalObject()
874
    trash = kw.get('trash', 0)
875 876 877 878 879
    trashbin = kw.get('trashbin', None)
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
Aurel's avatar
Aurel committed
880
      object_keys = self._archive.keys()
881
    for relative_url in object_keys:
882 883
      container_path = relative_url.split('/')[0:-1]
      object_id = relative_url.split('/')[-1]
884
      try:
885
        container = portal.unrestrictedTraverse(container_path)
886
        object = container._getOb(object_id) # We force access to the object to be sure
887 888
                                        # that appropriate exception is thrown
                                        # in case object is already backup and/or removed
889
        if trash and trashbin is not None:
890 891
          self.portal_trash.backupObject(trashbin, container_path, object_id, save=1, keep_subobjects=1)
        container.manage_delObjects([object_id])
892 893
        if container.aq_parent.meta_type == 'ERP5 Catalog' and len(container.objectIds()) == 0:
          # We are removing a ZSQLMethod, remove the SQLCatalog if empty
894
          container.getParentValue().manage_delObjects([container.id])
895
      except (NotFound, KeyError, BadRequest, AttributeError):
896
        # object is already backup and/or removed
897
        pass
898 899
    BaseTemplateItem.uninstall(self, context, **kw)

900 901 902 903 904 905 906 907 908 909 910 911
class PathTemplateItem(ObjectTemplateItem):
  """
    This class is used to store objects with wildcards supported.
  """
  def __init__(self, id_list, tool_id=None, **kw):
    BaseTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
    id_list = self._archive.keys()
    self._archive.clear()
    self._path_archive = PersistentMapping()
    for id in id_list:
      self._path_archive[id] = None

912 913 914 915 916 917 918 919 920
  def uninstall(self, context, **kw):
    portal = context.getPortalObject()
    trash = kw.get('trash', 0)
    trashbin = kw.get('trashbin', None)
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._path_archive.keys()
921 922
    object_keys.sort()
    object_keys.reverse()
923 924
    p = context.getPortalObject()
    for path in object_keys:
925 926 927 928 929
      try:
        path_list = self._resolvePath(p, [], path.split('/'))
      except AttributeError:
        # path seems to not exist anymore
        continue
930 931 932
      path_list.sort()
      path_list.reverse()
      for relative_url in path_list:
933
        try:
Aurel's avatar
Aurel committed
934 935
          container_path = relative_url.split('/')[0:-1]
          object_id = relative_url.split('/')[-1]
936 937
          container = portal.unrestrictedTraverse(container_path)
          if trash and trashbin is not None:
938 939
            self.portal_trash.backupObject(trashbin, container_path,
                                           object_id, save=1,
940
                                           keep_subobjects=1)
941 942 943 944 945 946
          container.manage_delObjects([object_id])
        except (NotFound, KeyError):
          # object is already backup and/or removed
          pass
    BaseTemplateItem.uninstall(self, context, **kw)

947 948 949
  def _resolvePath(self, folder, relative_url_list, id_list):
    """
      This method calls itself recursively.
950

951 952 953 954 955 956 957 958 959
      The folder is the current object which contains sub-objects.
      The list of ids are path components. If the list is empty,
      the current folder is valid.
    """
    if len(id_list) == 0:
      return ['/'.join(relative_url_list)]
    id = id_list[0]
    if re.search('[\*\?\[\]]', id) is None:
      # If the id has no meta character, do not have to check all objects.
960 961 962
      obj = folder._getOb(id, None)
      if obj is None:
        raise AttributeError, "Could not resolve '%s' during business template processing." % id
963
      return self._resolvePath(obj, relative_url_list + [id], id_list[1:])
964 965
    path_list = []
    for object_id in fnmatch.filter(folder.objectIds(), id):
966
      if object_id != "":
967
        path_list.extend(self._resolvePath(
968
            folder._getOb(object_id),
969
            relative_url_list + [object_id], id_list[1:]))
970
    return path_list
Aurel's avatar
Aurel committed
971

972 973 974
  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
Aurel's avatar
Aurel committed
975
    keys = self._path_archive.keys()
976
    keys.sort()
Aurel's avatar
Aurel committed
977
    for path in keys:
978 979 980
      include_subobjects = 0
      if '**' in path:
        include_subobjects = 1
981
      for relative_url in self._resolvePath(p, [], path.split('/')):
982 983
        obj = p.unrestrictedTraverse(relative_url)
        obj = obj._getCopy(context)
984
        obj = obj.__of__(context)
985
        _recursiveRemoveUid(obj)
986
        id_list = obj.objectIds()
987
        obj = self.removeProperties(obj)
988
        if hasattr(aq_base(obj), 'groups'):
989
          # we must keep groups because it's ereased when we delete subobjects
990
          groups = deepcopy(obj.groups)
991
        if len(id_list) > 0:
992 993
          if include_subobjects:
            self.build_sub_objects(context, id_list, relative_url)
994
          for id_ in list(id_list):
995
            obj._delObject(id_)
996
        if hasattr(aq_base(obj), 'groups'):
997 998 999
          obj.groups = groups
        self._objects[relative_url] = obj
        obj.wl_clearLocks()
1000

Yoshinori Okuji's avatar
Yoshinori Okuji committed
1001 1002 1003 1004 1005 1006 1007 1008
class ToolTemplateItem(PathTemplateItem):
  """This class is used only for making a distinction between other objects
  and tools, because tools may not be backed up."""
  def _backupObject(self, action, trashbin, container_path, object_id, **kw):
    """Fake as if a trashbin is not available."""
    return PathTemplateItem._backupObject(self, action, None, container_path,
                                          object_id, **kw)

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
class PreferenceTemplateItem(PathTemplateItem):
  """
  This class is used to store preference objects
  """
  def _resolvePath(self, folder, relative_url_list, id_list):
    """
    This method calls itself recursively.

    The folder is the current object which contains sub-objects.
    The list of ids are path components. If the list is empty,
    the current folder is valid.
    """
    if relative_url_list != []:
      LOG("PreferenceTemplateItem, _resolvePath", WARNING,
          "Should be empty")
1024
    if len(id_list) != 1:
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
      LOG("PreferenceTemplateItem, _resolvePath", WARNING,
          "Should contain only one element")
    # XXX hardcoded
    return ['portal_preferences/%s' % id_list[0]]

  def install(self, context, trashbin, **kw):
    """
    Enable Preference
    """
    PathTemplateItem.install(self, context, trashbin, **kw)
    portal = context.getPortalObject()
    for object_path in self._objects.keys():
1037
      pref = portal.unrestrictedTraverse(object_path)
1038
      # XXX getPreferenceState is a bad name
1039 1040 1041
      if pref.getPreferenceState() == 'disabled':
        portal.portal_workflow.doActionFor(
                      pref,
1042 1043 1044 1045 1046
                      'enable_action',
                      wf_id='preference_workflow',
                      comment="Initialized during Business Template " \
                              "installation.")

1047 1048
class CategoryTemplateItem(ObjectTemplateItem):

1049 1050
  def __init__(self, id_list, tool_id='portal_categories', **kw):
    ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
1051

1052 1053 1054 1055
  def build_sub_objects(self, context, id_list, url, **kw):
    p = context.getPortalObject()
    for id in id_list:
      relative_url = '/'.join([url,id])
1056 1057
      obj = p.unrestrictedTraverse(relative_url)
      obj = obj._getCopy(context)
1058
      obj = self.removeProperties(obj)
1059
      id_list = obj.objectIds()
1060
      if id_list:
1061
        self.build_sub_objects(context, id_list, relative_url)
1062
        for id_ in list(id_list):
1063
          obj._delObject(id_)
1064 1065
      self._objects[relative_url] = obj
      obj.wl_clearLocks()
1066 1067 1068 1069 1070

  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
    for relative_url in self._archive.keys():
1071 1072
      obj = p.unrestrictedTraverse(relative_url)
      obj = obj._getCopy(context)
1073
      _recursiveRemoveUid(obj)
1074
      obj = self.removeProperties(obj)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1075
      include_sub_categories = obj.__of__(context).getProperty('business_template_include_sub_categories', 0)
1076
      id_list = obj.objectIds()
1077 1078
      if len(id_list) > 0 and include_sub_categories:
        self.build_sub_objects(context, id_list, relative_url)
1079
        for id_ in list(id_list):
1080
          obj._delObject(id_)
1081
      else:
1082
        for id_ in list(id_list):
1083
          obj._delObject(id_)
1084 1085
      self._objects[relative_url] = obj
      obj.wl_clearLocks()
1086

1087
  def install(self, context, trashbin, **kw):
1088 1089
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
1090
    new_category = False
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1091
    if context.getTemplateFormatVersion() == 1:
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
      portal = context.getPortalObject()
      category_tool = portal.portal_categories
      tool_id = self.tool_id
      keys = self._objects.keys()
      keys.sort()
      for path in keys:
        if update_dict.has_key(path) or force:
          if not force:
            action = update_dict[path]
            if action == 'nothing':
              continue
          else:
            action = 'backup'
          # Wrap the object by an aquisition wrapper for _aq_dynamic.
          obj = self._objects[path]
          obj = obj.__of__(category_tool)
          container_path = path.split('/')[:-1]
          category_id = path.split('/')[-1]
          try:
            container = category_tool.unrestrictedTraverse(container_path)
          except KeyError:
            # parent object can be set to nothing, in this case just go on
            container_url = '/'.join(container_path)
            if update_dict.has_key(container_url):
              if update_dict[container_url] == 'nothing':
1117
                continue
1118 1119 1120 1121 1122 1123 1124 1125 1126
            raise
          container_ids = container.objectIds()
          # Object already exists
          object_uid = None
          subobjects_dict = {}
          if category_id in container_ids:
            object_uid = container[category_id].getUid()
            subobjects_dict = self._backupObject(action, trashbin, container_path, category_id)
            container.manage_delObjects([category_id])
1127 1128 1129
          else:
            # mark that we installed a new category to call aq_reset later
            new_category = True
1130 1131 1132
          category = container.newContent(portal_type=obj.getPortalType(), id=category_id)
          if object_uid is not None:
            category.setUid(object_uid)
1133 1134 1135 1136
          for prop in obj.propertyIds():
            if prop not in ('id', 'uid'):
              try:
                prop_value = obj.getProperty(prop, evaluate=0)
1137
              except TypeError: # the getter doesn't support evaluate=
1138 1139
                prop_value = obj.getProperty(prop)
              category.setProperty(prop, prop_value)
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
          # import sub objects if there is
          if len(subobjects_dict) > 0:
            # get a jar
            connection = obj._p_jar
            o = category
            while connection is None:
              o = o.aq_parent
              connection = o._p_jar
            # import subobjects
            for subobject_id in subobjects_dict.keys():
              subobject_data = subobjects_dict[subobject_id]
              subobject_data.seek(0)
              subobject = connection.importFile(subobject_data)
              if subobject_id not in category.objectIds():
                category._setObject(subobject_id, subobject)
1155
    else:
1156
      BaseTemplateItem.install(self, context, trashbin, **kw)
Aurel's avatar
Aurel committed
1157 1158 1159
      portal = context.getPortalObject()
      category_tool = portal.portal_categories
      tool_id = self.tool_id
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
      for relative_url in self._archive.keys():
        obj = self._archive[relative_url]
        # Wrap the object by an aquisition wrapper for _aq_dynamic.
        obj = obj.__of__(category_tool)
        container_path = relative_url.split('/')[0:-1]
        category_id = relative_url.split('/')[-1]
        container = category_tool.unrestrictedTraverse(container_path)
        container_ids = container.objectIds()
        if category_id in container_ids:    # Object already exists
          # XXX call backup here
          subobjects_dict = self._backupObject('backup', trashbin, container_path, category_id)
          container.manage_delObjects([category_id])
1172 1173 1174
        else:
          # mark that we installed a new category to call aq_reset later
          new_category = True
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
        category = container.newContent(portal_type=obj.getPortalType(), id=category_id)
        for prop in obj.propertyIds():
          if prop not in ('id', 'uid'):
            try:
              prop_value = obj.getProperty(prop, evaluate=0)
            except TypeError: # the getter doesn't support evaluate=
              prop_value = obj.getProperty(prop)
            category.setProperty(prop, prop_value)
        # import sub objects if there is
        if len(subobjects_dict) > 0:
          # get a jar
          connection = obj._p_jar
          o = category
          while connection is None:
            o = o.aq_parent
            connection = o._p_jar
          # import subobjects
          for subobject_id in subobjects_dict.keys():
            subobject_data = subobjects_dict[subobject_id]
            subobject_data.seek(0)
            subobject = connection.importFile(subobject_data)
            if subobject_id not in category.objectIds():
              category._setObject(subobject_id, subobject)
1198 1199 1200 1201
    if new_category:
      # reset accessors if we installed a new category
      _aq_reset()
              
1202

1203 1204
class SkinTemplateItem(ObjectTemplateItem):

1205 1206
  def __init__(self, id_list, tool_id='portal_skins', **kw):
    ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
1207

1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
  def preinstall(self, context, installed_bt, **kw):
    modified_object_list = ObjectTemplateItem.preinstall(self, context, installed_bt, **kw)
    # We must install/update an ERP5 Form if one of its widget is modified.
    # This allow to keep the widget order and the form layout after an update
    #   from a BT to another one.
    for (bt_obj_path, bt_obj) in self._objects.items():
      if getattr(bt_obj, 'meta_type', None) == 'ERP5 Form':
        # search sub-objects of ERP5 Forms that are marked as "modified"
        for upd_obj_path in modified_object_list.keys():
          if upd_obj_path.startswith(bt_obj_path):
            # a child of the ERP5 Form must be updated, so the form too
            if not modified_object_list.has_key(bt_obj_path):
              modified_object_list.update({bt_obj_path: ['Modified', self.__class__.__name__[:-12]]})
    return modified_object_list

1223
  def install(self, context, trashbin, **kw):
1224
    ObjectTemplateItem.install(self, context, trashbin, **kw)
1225 1226
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
1227 1228 1229
    p = context.getPortalObject()
    for relative_url in self._archive.keys():
      folder = p.unrestrictedTraverse(relative_url)
1230
      for obj in folder.objectValues(spec=('Z SQL Method',)):
1231 1232
        fixZSQLMethod(p, obj)

1233
    ps = p.portal_skins
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
    # Add new skin selection if not already existing
    for relative_url in self._archive.keys():
      if context.getTemplateFormatVersion() == 1:
        if update_dict.has_key(relative_url) or force:
          if not force:
            if update_dict[relative_url] == 'nothing':
              continue
        obj = self._objects[relative_url]
      else:
        obj = self._archive[relative_url]
      selection_list = obj.getProperty(
          'business_template_registered_skin_selections', [])
      if isinstance(selection_list, basestring):
        selection_list = selection_list.split()
      for skin_selection in selection_list:
        if skin_selection not in ps.getSkinSelections():
          # This skin selection does not exist, so we create a new one.
          # We'll initialize it with all skin folders, unless:
          #  - they explictly define a list of
          #    "business_template_registered_skin_selections", and we
          #    are not in this list.
          #  - they are not registred in the default skin selection
          skin_path = ''
          for skin_folder in ps.objectValues():
            if skin_selection in skin_folder.getProperty(
                     'business_template_registered_skin_selections',
                     (skin_selection, )):
              if skin_folder.getId() in ps.getSkinPath(ps.getDefaultSkin()):
                if skin_path:
                  skin_path = '%s,%s' % (skin_path, skin_folder.getId())
                else:
                  skin_path= skin_folder.getId()
          ps.addSkinSelection(skin_selection, skin_path)

    # Add new folders into skin paths.
1269 1270 1271 1272
    for skin_name, selection in ps.getSkinPaths():
      new_selection = []
      selection = selection.split(',')
      for relative_url in self._archive.keys():
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1273
        if context.getTemplateFormatVersion() == 1:
1274 1275
          if update_dict.has_key(relative_url) or force:
            if not force:
1276
              if update_dict[relative_url] == 'nothing':
1277
                continue
1278
          obj = self._objects[relative_url]
1279
        else:
1280
          obj = self._archive[relative_url]
1281
        skin_id = relative_url.split('/')[-1]
1282
        selection_list = obj.getProperty('business_template_registered_skin_selections', None)
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
        if selection_list is None or skin_name in selection_list:
          if skin_id not in selection:
            new_selection.append(skin_id)
      new_selection.extend(selection)
      # sort the layer according to skin priorities
      new_selection.sort(lambda a, b : cmp(
        b in ps.objectIds() and ps[b].getProperty(
            'business_template_skin_layer_priority', 0) or 0,
        a in ps.objectIds() and ps[a].getProperty(
            'business_template_skin_layer_priority', 0) or 0))
      ps.manage_skinLayers(skinpath = tuple(new_selection), skinname = skin_name, add_skin = 1)
    # Make sure that skin data is up-to-date (see CMFCore/Skinnable.py).
    p.changeSkin(None)
1296 1297 1298

  def uninstall(self, context, **kw):
    # Remove folders from skin paths.
1299 1300 1301 1302
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
1303
      object_keys = self._archive.keys()
1304
    ps = context.portal_skins
1305
    skin_id_list = [relative_url.split('/')[-1] for relative_url in object_keys]
1306 1307 1308 1309 1310 1311 1312
    for skin_name, selection in ps.getSkinPaths():
      new_selection = []
      selection = selection.split(',')
      for skin_id in selection:
        if skin_id not in skin_id_list:
          new_selection.append(skin_id)
      ps.manage_skinLayers(skinpath = tuple(new_selection), skinname = skin_name, add_skin = 1)
1313
    # Make sure that skin data is up-to-date (see CMFCore/Skinnable.py).
1314
    context.getPortalObject().changeSkin(None)
1315 1316 1317
    ObjectTemplateItem.uninstall(self, context, **kw)


1318
class WorkflowTemplateItem(ObjectTemplateItem):
1319

1320 1321
  def __init__(self, id_list, tool_id='portal_workflow', **kw):
    return ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
1322

1323
  def preinstall(self, context, installed_bt, **kw):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1324 1325
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
1326 1327 1328
      portal = context.getPortalObject()
      new_keys = self._objects.keys()
      for path in new_keys:
1329
        if installed_bt._objects.has_key(path):
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
          # compare object to see it there is changes
          new_object = self._objects[path]
          old_object = installed_bt._objects[path]
          new_io = StringIO()
          old_io = StringIO()
          OFS.XMLExportImport.exportXML(new_object._p_jar, new_object._p_oid, new_io)
          OFS.XMLExportImport.exportXML(old_object._p_jar, old_object._p_oid, old_io)
          new_obj_xml = new_io.getvalue()
          old_obj_xml = old_io.getvalue()
          new_io.close()
          old_io.close()
          if new_obj_xml != old_obj_xml:
            wf_id = path.split('/')[:2]
            modified_object_list.update({'/'.join(wf_id) : ['Modified', 'Workflow']})
        else: # new object
          modified_object_list.update({path : ['New', 'Workflow']})
1346 1347 1348 1349 1350 1351 1352 1353
      # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

  def install(self, context, trashbin, **kw):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1354
    if context.getTemplateFormatVersion() == 1:
1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
      portal = context.getPortalObject()
      # sort to add objects before their subobjects
      keys = self._objects.keys()
      keys.sort()
      update_dict = kw.get('object_to_update')
      force = kw.get('force')
      for path in keys:
        wf_path = '/'.join(path.split('/')[:2])
        if wf_path in update_dict or force:
          if not force:
            action = update_dict[wf_path]
            if action == 'nothing':
              continue
          else:
1369
            action = 'backup'
1370 1371 1372 1373 1374 1375
          container_path = path.split('/')[:-1]
          object_id = path.split('/')[-1]
          try:
            container = portal.unrestrictedTraverse(container_path)
          except KeyError:
            # parent object can be set to nothing, in this case just go on
1376
            container_url = '/'.join(container_path)
1377 1378 1379 1380 1381 1382
            if update_dict.has_key(container_url):
              if update_dict[container_url] == 'nothing':
                continue
            raise
          container_ids = container.objectIds()
          if object_id in container_ids:    # Object already exists
1383
            self._backupObject(action, trashbin, container_path, object_id, keep_subobjects=1)
1384
            container.manage_delObjects([object_id])
1385
          obj = self._objects[path]
1386 1387 1388
          if getattr(obj, 'meta_type', None) == 'Script (Python)':
            if getattr(obj, '_code') is None:
              obj._compile()
1389 1390 1391 1392 1393
          obj = obj._getCopy(container)
          container._setObject(object_id, obj)
          obj = container._getOb(object_id)
          obj.manage_afterClone(obj)
          obj.wl_clearLocks()
1394 1395 1396 1397
    else:
      ObjectTemplateItem.install(self, context, trashbin, **kw)


1398 1399
class PortalTypeTemplateItem(ObjectTemplateItem):

1400 1401
  def __init__(self, id_list, tool_id='portal_types', **kw):
    ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
1402 1403
    # XXX : this statement can be removed once all bt5 have separated
    # workflow-chain information
1404 1405 1406
    self._workflow_chain_archive = PersistentMapping()

  def build(self, context, **kw):
1407 1408
    p = context.getPortalObject()
    for relative_url in self._archive.keys():
1409 1410
      obj = p.unrestrictedTraverse(relative_url)
      obj = obj._getCopy(context)
1411 1412 1413
      # remove actions and properties
      action_len = len(obj.listActions())
      obj.deleteActions(selections=range(action_len))
1414
      obj = self.removeProperties(obj)
1415 1416 1417 1418 1419 1420 1421 1422 1423
      # remove some properties
      if hasattr(obj, 'allowed_content_types'):
        setattr(obj, 'allowed_content_types', ())
      if hasattr(obj, 'hidden_content_type_list'):
        setattr(obj, 'hidden_content_type_list', ())
      if hasattr(obj, 'property_sheet_list'):
        setattr(obj, 'property_sheet_list', ())
      if hasattr(obj, 'base_category_list'):
        setattr(obj, 'base_category_list', ())
Alexandre Boeglin's avatar
Alexandre Boeglin committed
1424 1425
      if hasattr(obj, '_roles'):
        setattr(obj, '_roles', [])
1426 1427
      self._objects[relative_url] = obj
      obj.wl_clearLocks()
1428

1429 1430
  # XXX : this method is kept temporarily, but can be removed once all bt5 are
  # re-exported with separated workflow-chain information
1431 1432 1433 1434
  def install(self, context, trashbin, **kw):
    ObjectTemplateItem.install(self, context, trashbin, **kw)
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
1435 1436
    # We now need to setup the list of workflows corresponding to
    # each portal type
1437
    (default_chain, chain_dict) = getChainByType(context)
1438
    # Set the default chain to the empty string is probably the
1439
    # best solution, by default it is 'default_workflow', which is
1440
    # not very usefull
1441
    default_chain = ''
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1442
    if context.getTemplateFormatVersion() == 1:
1443
      object_list = self._objects
1444
    else:
1445 1446
      object_list = self._archive
    for path in object_list.keys():
1447 1448 1449 1450
      if update_dict.has_key(path) or force:
        if not force:
          action = update_dict[path]
          if action == 'nothing':
1451
            continue
1452 1453
        obj = object_list[path]
        portal_type = obj.id
1454 1455 1456
        if self._workflow_chain_archive.has_key(portal_type):
          chain_dict['chain_%s' % portal_type] = \
              self._workflow_chain_archive[portal_type]
1457 1458
    context.portal_workflow.manage_changeWorkflows(default_chain,
                                                   props=chain_dict)
1459

1460 1461
  # XXX : this method is kept temporarily, but can be removed once all bt5 are
  # re-exported with separated workflow-chain information
1462 1463 1464 1465 1466 1467 1468
  def _importFile(self, file_name, file):
    if 'workflow_chain_type.xml' in file_name:
      # import workflow chain for portal_type
      dict = {}
      xml = parse(file)
      chain_list = xml.getElementsByTagName('chain')
      for chain in chain_list:
1469
        ptype = chain.getElementsByTagName('type')[0].childNodes[0].data
1470 1471 1472 1473 1474
        workflow_list = chain.getElementsByTagName('workflow')[0].childNodes
        if len(workflow_list) == 0:
          workflow = ''
        else:
          workflow = workflow_list[0].data
1475
        dict[str(ptype)] = str(workflow)
1476 1477 1478 1479
      self._workflow_chain_archive = dict
    else:
      ObjectTemplateItem._importFile(self, file_name, file)

1480
class PortalTypeWorkflowChainTemplateItem(BaseTemplateItem):
1481 1482

  def build(self, context, **kw):
1483 1484 1485 1486 1487
    # we can either specify nothing, +, - or = before the chain
    # this is used to know how to manage the chain
    # if nothing or +, chain is added to the existing one
    # if - chain is removed from the exisiting one
    # if = chain replaced the existing one
1488
    p = context.getPortalObject()
1489
    (default_chain, chain_dict) = getChainByType(context)
Aurel's avatar
Aurel committed
1490
    for key in self._archive.keys():
1491 1492 1493
      wflist = key.split(' | ')
      if len(wflist) == 2:
        portal_type = wflist[0]
Aurel's avatar
Aurel committed
1494
        workflow = wflist[1]
1495
      else:
1496
        # portal type with no workflow defined
Aurel's avatar
Aurel committed
1497
        portal_type = wflist[0][:-2]
1498 1499
        workflow = ''
      if chain_dict.has_key('chain_%s' % portal_type):
1500 1501 1502 1503
        if workflow[0] in ['+', '-', '=']:
          workflow_name = workflow[1:]
        else:
          workflow_name = workflow
1504
        if workflow[0] != '-' and \
1505
            workflow_name not in chain_dict['chain_%s' % portal_type]:
Alexandre Boeglin's avatar
Alexandre Boeglin committed
1506
          raise NotFound, 'workflow %s not found in chain for portal_type %s'\
1507
                % (workflow, portal_type)
1508
        if self._objects.has_key(portal_type):
1509 1510
          # other workflow id already defined for this portal type
          self._objects[portal_type].append(workflow)
1511
        else:
1512
          self._objects[portal_type] = [workflow,]
1513
      else:
1514 1515
        raise NotFound, 'portal type %s not found in workflow chain'\
                                                    % portal_type
1516

Christophe Dumez's avatar
Christophe Dumez committed
1517
  # Function to generate XML Code Manually
1518 1519 1520 1521 1522
  def generateXml(self, path=None):
    xml_data = '<workflow_chain>'
    keys = self._objects.keys()
    keys.sort()
    for key in keys:
1523
      workflow_list = self._objects[key]
1524 1525 1526
      # XXX Not always a list
      if isinstance(workflow_list, str):
        workflow_list = [workflow_list]
1527 1528 1529 1530 1531
      xml_data += '\n <chain>'
      xml_data += '\n  <type>%s</type>' %(key,)
      xml_data += '\n  <workflow>%s</workflow>' %(', '.join(workflow_list))
      xml_data += '\n </chain>'
    xml_data += '\n</workflow_chain>'
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=root_path)
    # export workflow chain
    xml_data = self.generateXml()
    bta.addObject(obj=xml_data, name='workflow_chain_type',  path=root_path)

  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # We now need to setup the list of workflows corresponding to
    # each portal type
1548
    (default_chain, chain_dict) = getChainByType(context)
1549
    # Set the default chain to the empty string is probably the
Christophe Dumez's avatar
Christophe Dumez committed
1550
    # best solution, by default it is 'default_workflow', which is
1551 1552 1553 1554 1555 1556 1557
    # not very usefull
    default_chain = ''
    for path in self._objects.keys():
      if update_dict.has_key(path) or force:
        if not force:
          action = update_dict[path]
          if action == 'nothing':
1558
            continue
Jérome Perrin's avatar
Jérome Perrin committed
1559 1560
        path_splitted = path.split('/', 1)
        # XXX: to avoid crashing when no portal_type
1561
        if len(path_splitted) < 1:
Jérome Perrin's avatar
Jérome Perrin committed
1562
          continue
1563
        portal_type = path_splitted[-1]
1564
        if chain_dict.has_key('chain_%s' % portal_type):
Aurel's avatar
Aurel committed
1565 1566 1567
          old_chain_dict = chain_dict['chain_%s' % portal_type]
          # XXX we don't use the chain (Default) in erp5 so don't keep it
          if old_chain_dict != '(Default)' and old_chain_dict != '':
1568
            old_chain_workflow_id_set = {}
1569
            # get existent workflow id list
1570 1571
            for wf_id in old_chain_dict.split(', '):
              old_chain_workflow_id_set[wf_id] = 1
1572
            # get new workflow id list
1573
            for wf_id in self._objects[path].split(', '):
1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
              if wf_id[0] == '-':
                # remove wf id if already present
                if old_chain_workflow_id_set.has_key(wf_id[1:]):
                  old_chain_workflow_id_set.pop(wf_id[1:])
              elif wf_id[0] == '=':
                # replace existing chain by this one
                old_chain_workflow_id_set = {}
                old_chain_workflow_id_set[wf_id[1:]] = 1
              # then either '+' or nothing, add wf id to the list
              elif wf_id[0] == '+':
                old_chain_workflow_id_set[wf_id[1:]] = 1
              else:
                old_chain_workflow_id_set[wf_id] = 1
            # create the new chain
1588 1589
            chain_dict['chain_%s' % portal_type] = ', '.join(
                                              old_chain_workflow_id_set.keys())
Aurel's avatar
Aurel committed
1590
          else:
Romain Courteaud's avatar
Romain Courteaud committed
1591 1592 1593 1594 1595 1596
            # Check if it has normally to remove a workflow chain, in order to
            # improve the error message
            for wf_id in self._objects[path].split(', '):
              if wf_id.startswith('-'):
                raise ValueError, '"%s" is not a workflow ID for %s' % \
                                  (wf_id, portal_type)
1597
            chain_dict['chain_%s' % portal_type] = self._objects[path]
Aurel's avatar
Aurel committed
1598 1599
        else:
          chain_dict['chain_%s' % portal_type] = self._objects[path]
1600 1601
    context.portal_workflow.manage_changeWorkflows(default_chain,
                                                   props=chain_dict)
1602

1603 1604
  def uninstall(self, context, **kw):
    (default_chain, chain_dict) = getChainByType(context)
1605 1606 1607 1608 1609 1610
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._objects.keys()
    for path in object_keys:
1611 1612 1613 1614 1615 1616
      path_splitted = path.split('/', 1)
      if len(path_splitted) < 2:
        continue
      portal_type = path_splitted[1]
      id = 'chain_%s' % portal_type
      if id in chain_dict.keys():
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
        chain = chain_dict[id]
        # It should be better to use regexp
        chain = chain.replace(' ', '')
        workflow_list = chain.split(',')
        workflow_id = self._objects[path]
        for i in range(workflow_list.count(workflow_id)):
          workflow_list.remove(workflow_id)
        chain = ', '.join(workflow_list)
        if chain == '':
          del chain_dict[id]
        else:
          chain_dict[id] = chain
1629
    context.portal_workflow.manage_changeWorkflows('', props=chain_dict)
1630

1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
  def preinstall(self, context, installed_bt, **kw):
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
      new_keys = self._objects.keys()
      new_dict = PersistentMapping()
      # Fix key from installed bt if necessary
      for key in installed_bt._objects.keys():
        if not "portal_type_workflow_chain/" in key:
          new_key = 'portal_type_workflow_chain/%s' %key
          new_dict[new_key] = installed_bt._objects[key]
        else:
          new_dict[key] = installed_bt._objects[key]
      if len(new_dict):
        installed_bt._objects = new_dict
      for path in new_keys:
        if installed_bt._objects.has_key(path):
          # compare object to see it there is changes
          new_object = self._objects[path]
          old_object = installed_bt._objects[path]
          # compare same type of object
          if isinstance(old_object, list) or isinstance(old_object, tuple):
            old_object = ', '.join(old_object)
          if new_object != old_object:
            modified_object_list.update({path : ['Modified', self.__class__.__name__[:-12]]})
        else: # new object
          modified_object_list.update({path : ['New', self.__class__.__name__[:-12]]})
      # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675
  def _importFile(self, file_name, file):
    # import workflow chain for portal_type
    dict = {}
    xml = parse(file)
    chain_list = xml.getElementsByTagName('chain')
    for chain in chain_list:
      ptype = chain.getElementsByTagName('type')[0].childNodes[0].data
      workflow_list = chain.getElementsByTagName('workflow')[0].childNodes
      if len(workflow_list) == 0:
        workflow = ''
      else:
        workflow = workflow_list[0].data
1676 1677
      if 'portal_type_workflow_chain/' not in str(ptype):
        ptype = 'portal_type_workflow_chain/' + str(ptype)
1678 1679 1680
      dict[str(ptype)] = str(workflow)
    self._objects = dict

1681 1682
# just for backward compatibility
PortalTypeTemplateWorkflowChainItem = PortalTypeWorkflowChainTemplateItem
1683

1684
class PortalTypeAllowedContentTypeTemplateItem(BaseTemplateItem):
1685 1686 1687
  # XXX This class is subclassed for hidden types, propertysheets, base
  # categories ...
  name = 'Allowed Content Type'
1688 1689 1690 1691
  xml_tag = 'allowed_content_type_list'
  class_property = 'allowed_content_types'

  def build(self, context, **kw):
1692 1693
    types_tool = self.getPortalObject().portal_types
    types_list = list(types_tool.objectIds())
1694
    for key in self._archive.keys():
1695
      portal_type, allowed_type = key.split(' | ')
1696 1697 1698 1699 1700 1701
      # check properties corresponds to what is defined in site
      if not portal_type in types_list:
        raise ValueError, "Portal Type %s not found in site" %(portal_type,)
      ob = types_tool._getOb(portal_type)
      prop_value = getattr(ob, self.class_property, ())
      if not allowed_type in prop_value:
1702 1703 1704 1705
        raise ValueError, "%s  %s not found in portal type %s" % (
                             getattr(self, 'name', self.__class__.__name__),
                             allowed_type, portal_type)

1706 1707 1708 1709 1710 1711 1712
      if self._objects.has_key(portal_type):
        allowed_list = self._objects[portal_type]
        allowed_list.append(allowed_type)
        self._objects[portal_type] = allowed_list
      else:
        self._objects[portal_type] = [allowed_type]

Christophe Dumez's avatar
Christophe Dumez committed
1713
  # Function to generate XML Code Manually
1714
  def generateXml(self, path=None):
1715
    dictio = self._objects
1716
    xml_data = '<%s>' %(self.xml_tag,)
1717
    keys = dictio.keys()
1718 1719
    keys.sort()
    for key in keys:
1720
      allowed_list = dictio[key]
1721
      xml_data += '\n <portal_type id="%s">' %(key,)
1722
      for allowed_item in allowed_list:
1723 1724 1725
        xml_data += '\n  <item>%s</item>' %(allowed_item,)
      xml_data += '\n </portal_type>'
    xml_data += '\n</%s>' %(self.xml_tag,)
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    path = self.__class__.__name__+os.sep+self.class_property
    xml_data = self.generateXml(path=None)
    bta.addObject(obj=xml_data, name=path, path=None)

1737 1738 1739 1740 1741
  def preinstall(self, context, installed_bt, **kw):
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
      portal = context.getPortalObject()
      new_keys = self._objects.keys()
1742 1743 1744 1745
      new_dict = PersistentMapping()
      # fix key if necessary in installed bt for diff
      for key in installed_bt._objects.keys():
        if self.class_property not in key:
1746
          new_key = '%s/%s' % (self.class_property, key)
1747
          new_dict[new_key] = installed_bt._objects[key]
1748 1749 1750
        else:
          new_dict[key] = installed_bt._objects[key]
      if len(new_dict):
1751 1752 1753 1754 1755
        installed_bt._objects = new_dict
      for path in new_keys:
        if installed_bt._objects.has_key(path):
          # compare object to see it there is changes
          new_object = self._objects[path]
1756
          old_object = installed_bt._objects[path]
1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
          if new_object != old_object:
            modified_object_list.update({path : ['Modified', self.__class__.__name__[:-12]]})
        else: # new object
          modified_object_list.update({path : ['New', self.__class__.__name__[:-12]]})
      # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

1768
  def _importFile(self, file_name, file):
1769
    path, name = posixpath.split(file_name)
1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793
    xml = parse(file)
    portal_type_list = xml.getElementsByTagName('portal_type')
    for portal_type in portal_type_list:
      id = portal_type.getAttribute('id')
      item_type_list = []
      item_list = portal_type.getElementsByTagName('item')
      for item in item_list:
        item_type_list.append(str(item.childNodes[0].data))
      self._objects[self.class_property+'/'+id] = item_type_list

  def install(self, context, trashbin, **kw):
    p = context.getPortalObject()
    pt = p.unrestrictedTraverse('portal_types')
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    for key in self._objects.keys():
      if update_dict.has_key(key) or force:
        if not force:
          action = update_dict[key]
          if action == 'nothing':
            continue
        try:
          portal_id = key.split('/')[-1]
          portal_type = pt._getOb(portal_id)
1794
        except AttributeError:
1795 1796 1797 1798 1799 1800 1801 1802
          LOG("portal types not found : ", 100, portal_id)
          continue
        property_list = self._objects[key]
        object_property_list = getattr(portal_type, self.class_property, ())
        if len(object_property_list) > 0:
          # merge differences between portal types properties
          # only add new, do not remove
          for id in object_property_list:
1803 1804
            if id not in property_list:
              property_list.append(id)
1805 1806
        setattr(portal_type, self.class_property, list(property_list))

Aurel's avatar
Aurel committed
1807
  def uninstall(self, context, **kw):
1808
    object_path = kw.get('object_path', None)
1809
    p = context.getPortalObject()
1810
    pt = p.unrestrictedTraverse('portal_types')
1811 1812 1813 1814 1815 1816 1817 1818
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._objects.keys()
    for key in object_keys:
      try:
        portal_id = key.split('/')[-1]
        portal_type = pt._getOb(portal_id)
1819
      except AttributeError:
1820 1821 1822
        LOG("portal types not found : ", 100, portal_id)
        continue
      property_list = self._objects[key]
1823 1824
      original_property_list = list(getattr(portal_type,
                                    self.class_property, ()))
Aurel's avatar
Aurel committed
1825 1826
      for id in property_list:
        if id in original_property_list:
1827
          original_property_list.remove(id)
1828
      setattr(portal_type, self.class_property, list(original_property_list))
1829

1830

1831 1832
class PortalTypeHiddenContentTypeTemplateItem(PortalTypeAllowedContentTypeTemplateItem):

1833
  name = 'Hidden Content Type'
1834 1835 1836
  xml_tag = 'hidden_content_type_list'
  class_property = 'hidden_content_type_list'

1837

1838 1839
class PortalTypePropertySheetTemplateItem(PortalTypeAllowedContentTypeTemplateItem):

1840
  name = 'Property Sheet'
1841 1842 1843
  xml_tag = 'property_sheet_list'
  class_property = 'property_sheet_list'

1844

1845 1846
class PortalTypeBaseCategoryTemplateItem(PortalTypeAllowedContentTypeTemplateItem):

1847
  name = 'Base Category'
1848 1849 1850
  xml_tag = 'base_category_list'
  class_property = 'base_category_list'

1851

1852
class CatalogMethodTemplateItem(ObjectTemplateItem):
1853
  """Template Item for catalog methods.
1854

1855 1856 1857 1858 1859
    This template item stores catalog method and install them in the
    default catalog.
    The use Catalog makes for methods is saved as well and recreated on
    installation.
  """
1860

1861 1862
  def __init__(self, id_list, tool_id='portal_catalog', **kw):
    ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw)
1863
    # a mapping to store properties of methods.
1864 1865 1866 1867 1868
    # the mapping contains an entry for each method, and this entry is
    # another mapping having the id of the catalog property as key and a
    # boolean value to say wether the method is part of this catalog
    # configuration property.
    self._method_properties = PersistentMapping()
1869

1870 1871 1872 1873 1874
    self._is_filtered_archive = PersistentMapping()
    self._filter_expression_archive = PersistentMapping()
    self._filter_expression_instance_archive = PersistentMapping()
    self._filter_type_archive = PersistentMapping()

1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888
  def _extractMethodProperties(self, catalog, method_id):
    """Extracts properties for a given method in the catalog.
    Returns a mapping of property name -> boolean """
    method_properties = PersistentMapping()
    for prop in catalog._properties:
      if prop.get('select_variable') == 'getCatalogMethodIds':
        if prop['type'] == 'selection' and \
            getattr(catalog, prop['id']) == method_id:
          method_properties[prop['id']] = 1
        elif prop['type'] == 'multiple selection' and \
            method_id in getattr(catalog, prop['id']):
          method_properties[prop['id']] = 1
    return method_properties

1889 1890
  def build(self, context, **kw):
    ObjectTemplateItem.build(self, context, **kw)
1891

1892
    catalog = _getCatalogValue(self)
1893
    if catalog is None:
1894
      LOG('BusinessTemplate build', 0, 'catalog not found')
1895
      return
1896

1897 1898 1899
    # upgrade old
    if not hasattr(self, '_method_properties'):
      self._method_properties = PersistentMapping()
1900

1901 1902
    for obj in self._objects.values():
      method_id = obj.id
1903 1904
      self._method_properties[method_id] = self._extractMethodProperties(
                                                          catalog, method_id)
1905
      self._is_filtered_archive[method_id] = 0
1906
      if catalog.filter_dict.has_key(method_id):
1907 1908 1909 1910 1911 1912 1913 1914 1915
        if catalog.filter_dict[method_id]['filtered']:
          self._is_filtered_archive[method_id] = \
                      catalog.filter_dict[method_id]['filtered']
          self._filter_expression_archive[method_id] = \
                      catalog.filter_dict[method_id]['expression']
          self._filter_expression_instance_archive[method_id] = \
                      catalog.filter_dict[method_id]['expression_instance']
          self._filter_type_archive[method_id] = \
                      catalog.filter_dict[method_id]['type']
1916

Aurel's avatar
Aurel committed
1917
  def export(self, context, bta, **kw):
1918
    catalog = _getCatalogValue(self)
1919 1920 1921 1922
    if catalog is None:
      LOG('BusinessTemplate, export', 0, 'no SQL catalog was available')
      return

Aurel's avatar
Aurel committed
1923 1924 1925 1926
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    for key in self._objects.keys():
1927
      obj = self._objects[key]
Aurel's avatar
Aurel committed
1928
      # create folder and subfolders
1929
      folders, id = posixpath.split(key)
Aurel's avatar
Aurel committed
1930 1931 1932 1933
      path = os.path.join(root_path, folders)
      bta.addFolder(name=path)
      # export object in xml
      f=StringIO()
1934 1935
      XMLExportImport.exportXML(obj._p_jar, obj._p_oid, f)
      bta.addObject(obj=f.getvalue(), name=id, path=path)
Aurel's avatar
Aurel committed
1936
      # add all datas specific to catalog inside one file
1937
      method_id = obj.id
1938 1939
      object_path = os.path.join(path, method_id+'.catalog_keys.xml')

1940
      f = open(object_path, 'wb')
1941
      xml_data = '<catalog_method>'
1942

1943
      for method_property, value in self._method_properties[method_id].items():
1944 1945 1946
        xml_data += '\n <item key="%s" type="int">' %(method_property,)
        xml_data += '\n  <value>%s</value>' %(value,)
        xml_data += '\n </item>'
1947

Aurel's avatar
Aurel committed
1948
      if catalog.filter_dict.has_key(method_id):
1949
        if catalog.filter_dict[method_id]['filtered']:
1950 1951 1952
          xml_data += '\n <item key="_is_filtered_archive" type="int">'
          xml_data += '\n  <value>1</value>'
          xml_data += '\n </item>'
1953 1954 1955 1956
          for method in catalog_method_filter_list:
            value = getattr(self, method, '')[method_id]
            if method != '_filter_expression_instance_archive':
              if type(value) in (type(''), type(u'')):
1957 1958 1959
                xml_data += '\n <item key="%s" type="str">' %(method,)
                xml_data += '\n  <value>%s</value>' %(str(value))
                xml_data += '\n </item>'
1960
              elif type(value) in (type(()), type([])):
1961
                xml_data += '\n <item key="%s" type="tuple">'%(method)
1962
                for item in value:
1963 1964 1965
                  xml_data += '\n  <value>%s</value>' %(str(item))
                xml_data += '\n </item>'
      xml_data += '\n</catalog_method>'
1966
      f.write(xml_data)
Aurel's avatar
Aurel committed
1967
      f.close()
1968

Christophe Dumez's avatar
Christophe Dumez committed
1969 1970 1971 1972 1973 1974
  # Function to generate XML Code Manually
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
1975 1976
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
Christophe Dumez's avatar
Christophe Dumez committed
1977
    return xml_data
1978

1979 1980
  def install(self, context, trashbin, **kw):
    ObjectTemplateItem.install(self, context, trashbin, **kw)
1981
    catalog = _getCatalogValue(self)
1982 1983 1984 1985 1986 1987 1988
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    # Make copies of attributes of the default catalog of portal_catalog.
    sql_catalog_object_list = list(catalog.sql_catalog_object_list)
    sql_uncatalog_object = list(catalog.sql_uncatalog_object)
    sql_clear_catalog = list(catalog.sql_clear_catalog)
1989

1990 1991 1992
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    values = []
Yoshinori Okuji's avatar
Yoshinori Okuji committed
1993
    new_bt_format = context.getTemplateFormatVersion()
1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009

    if force: # get all objects
      if new_bt_format:
        values = self._objects.values()
      else:
        values = self._archive.values()
    else: # get only selected object
      if new_bt_format == 1:
        keys = self._objects.keys()
      else:
        keys = self._archive.keys()
      for key in keys:
        if update_dict.has_key(key) or force:
          if not force:
            action = update_dict[key]
            if action == 'nothing':
2010
              continue
2011 2012 2013 2014
          if new_bt_format:
            values.append(self._objects[key])
          else:
            values.append(self._archive[key])
2015

2016 2017
    for obj in values:
      method_id = obj.id
2018

2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029
      # Restore catalog properties for methods
      if hasattr(self, '_method_properties'):
        for key in self._method_properties.get(method_id, {}).keys():
          old_value = getattr(catalog, key, None)
          if isinstance(old_value, str):
            setattr(catalog, key, method_id)
          elif isinstance(old_value, list) or isinstance(old_value, tuple):
            if method_id not in old_value:
              new_value = list(old_value) + [method_id]
              new_value.sort()
              setattr(catalog, key, tuple(new_value))
2030

2031 2032
      # Restore filter
      if self._is_filtered_archive.get(method_id, 0):
2033
        expression = self._filter_expression_archive[method_id]
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2034
        if context.getTemplateFormatVersion() == 1:
2035 2036 2037
          expr_instance = Expression(expression)
        else:
          expr_instance = self._filter_expression_instance_archive[method_id]
2038
        filter_type = self._filter_type_archive[method_id]
2039 2040 2041
        catalog.filter_dict[method_id] = PersistentMapping()
        catalog.filter_dict[method_id]['filtered'] = 1
        catalog.filter_dict[method_id]['expression'] = expression
2042
        catalog.filter_dict[method_id]['expression_instance'] = expr_instance
2043
        catalog.filter_dict[method_id]['type'] = filter_type
2044
      elif method_id in catalog.filter_dict.keys():
2045
        catalog.filter_dict[method_id]['filtered'] = 0
2046

2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072
      # backward compatibility
      if hasattr(self, '_is_catalog_list_method_archive'):
        LOG("BusinessTemplate.CatalogMethodTemplateItem", 0,
            "installing old style catalog method configuration")
        is_catalog_list_method = int(
                  self._is_catalog_list_method_archive[method_id])
        is_uncatalog_method = int(
                  self._is_uncatalog_method_archive[method_id])
        is_clear_method = int(
                  self._is_clear_method_archive[method_id])

        if is_catalog_list_method and method_id not in sql_catalog_object_list:
          sql_catalog_object_list.append(method_id)
        elif not is_catalog_list_method and\
                        method_id in sql_catalog_object_list:
          sql_catalog_object_list.remove(method_id)

        if is_uncatalog_method and method_id not in sql_uncatalog_object:
          sql_uncatalog_object.append(method_id)
        elif not is_uncatalog_method and method_id in sql_uncatalog_object:
          sql_uncatalog_object.remove(method_id)

        if is_clear_method and method_id not in sql_clear_catalog:
          sql_clear_catalog.append(method_id)
        elif not is_clear_method and method_id in sql_clear_catalog:
          sql_clear_catalog.remove(method_id)
2073

2074 2075 2076 2077 2078 2079
        sql_catalog_object_list.sort()
        catalog.sql_catalog_object_list = tuple(sql_catalog_object_list)
        sql_uncatalog_object.sort()
        catalog.sql_uncatalog_object = tuple(sql_uncatalog_object)
        sql_clear_catalog.sort()
        catalog.sql_clear_catalog = tuple(sql_clear_catalog)
2080

2081
  def uninstall(self, context, **kw):
2082
    catalog = _getCatalogValue(self)
2083 2084 2085
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
2086

2087 2088 2089 2090
    values = []
    object_path = kw.get('object_path', None)
    # get required values
    if object_path is None:
2091 2092 2093 2094
      if context.getTemplateFormatVersion() == 1:
        values = self._objects.values()
      else:
        values = self._archive.values()
2095
    else:
2096 2097 2098 2099
      try:
        value = self._archive[object_path]
      except KeyError:
        value = None
2100 2101
      if value is not None:
        values.append(value)
2102
    for obj in values:
2103
      method_id = obj.id
2104 2105 2106 2107 2108 2109 2110 2111 2112
      # remove method references in portal_catalog
      for catalog_prop in catalog._properties:
        if catalog_prop.get('select_variable') == 'getCatalogMethodIds'\
            and catalog_prop['type'] == 'multiple selection':
          old_value = getattr(catalog, catalog_prop['id'], ())
          if method_id in old_value:
            new_value = list(old_value)
            new_value.remove(method_id)
            setattr(catalog, catalog_prop['id'], new_value)
2113

Yoshinori Okuji's avatar
Yoshinori Okuji committed
2114
      if catalog.filter_dict.has_key(method_id):
2115
        del catalog.filter_dict[method_id]
2116

2117
    # uninstall objects
2118
    ObjectTemplateItem.uninstall(self, context, **kw)
2119

2120
  def _importFile(self, file_name, file):
2121
    if not '.catalog_keys' in file_name:
2122 2123 2124 2125 2126 2127 2128
      # just import xml object
      obj = self
      connection = None
      while connection is None:
        obj=obj.aq_parent
        connection=obj._p_jar
      obj = connection.importFile(file, customImporters=customImporters)
2129
      self.removeProperties(obj)
2130
      self._objects[file_name[:-4]] = obj
2131
    else:
2132
      # recreate data mapping specific to catalog method
2133 2134
      name = os.path.basename(file_name)
      id = name.split('.', 1)[0]
2135
      xml = parse(file)
2136
      method_list = xml.getElementsByTagName('item')
2137
      for method in method_list:
2138
        key = method.getAttribute('key')
2139 2140
        key_type = str(method.getAttribute('type'))
        if key_type == "str":
2141 2142 2143 2144
          if len(method.getElementsByTagName('value')[0].childNodes):
            value = str(method.getElementsByTagName('value')[0].childNodes[0].data)
          else:
            value = ''
2145
          key = str(key)
2146
        elif key_type == "int":
2147
          value = int(method.getElementsByTagName('value')[0].childNodes[0].data)
2148
          key = str(key)
2149
        elif key_type == "tuple":
2150 2151 2152 2153
          value = []
          value_list = method.getElementsByTagName('value')
          for item in value_list:
            value.append(item.childNodes[0].data)
2154
        else:
2155
          LOG('BusinessTemplate import CatalogMethod, type unknown', 0, key_type)
2156
          continue
2157
        if key in catalog_method_list or key in catalog_method_filter_list:
2158
          dict = getattr(self, key, {})
2159
          dict[id] = value
2160 2161 2162
        else:
          # new style key
          self._method_properties.setdefault(id, PersistentMapping())[key] = 1
2163

2164
class ActionTemplateItem(ObjectTemplateItem):
2165 2166

  def __init__(self, id_list, **kw):
2167
    # XXX It's look like ObjectTemplateItem __init__
2168 2169 2170 2171 2172 2173
    BaseTemplateItem.__init__(self, id_list, **kw)
    id_list = self._archive.keys()
    self._archive.clear()
    for id in id_list:
      self._archive["%s/%s" % ('portal_types', id)] = None

2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
  def _splitPath(self, path):
    """
      Split path tries to split a complexe path such as:

      "foo/bar[id=zoo]"

      into

      "foo/bar", "id", "zoo"

      This is used mostly for generic objects
    """
    # Add error checking here
    if path.find('[') >= 0 and path.find(']') > path.find('=') and path.find('=') > path.find('['):
      relative_url = path[0:path.find('[')]
      id_block = path[path.find('[')+1:path.find(']')]
      key = id_block.split('=')[0]
      value = id_block.split('=')[1]
      return relative_url, key, value
    return path, None, None

2195 2196 2197 2198
  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
    for id in self._archive.keys():
2199
      relative_url, value = id.split(' | ')
2200 2201
      obj = p.unrestrictedTraverse(relative_url)
      for ai in obj.listActions():
2202
        if getattr(ai, 'id') == value:
2203 2204
          url = posixpath.split(relative_url)
          key = posixpath.join(url[-2], url[-1], value)
2205
          action = ai._getCopy(context)
2206
          action = self.removeProperties(action)
2207
          self._objects[key] = action
Aurel's avatar
Aurel committed
2208
          self._objects[key].wl_clearLocks()
2209 2210
          break
      else:
2211
        raise NotFound, 'Action %r not found' %(id,)
Aurel's avatar
Aurel committed
2212

2213 2214 2215
  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2216
    if context.getTemplateFormatVersion() == 1:
Aurel's avatar
Aurel committed
2217 2218
      p = context.getPortalObject()
      for id in self._objects.keys():
2219 2220 2221 2222 2223
        if update_dict.has_key(id) or force:
          if not force:
            action = update_dict[id]
            if action == 'nothing':
              continue
2224
          path = id.split('/')
2225 2226
          obj = p.unrestrictedTraverse(path[:-1])
          action_list = obj.listActions()
2227
          for index in range(len(action_list)):
2228
            if getattr(action_list[index], 'id') == path[-1]:
2229
              # remove previous action
2230
              obj.deleteActions(selections=(index,))
2231
          action = self._objects[id]
2232 2233 2234
          action_text = action.action
          if isinstance(action_text, Expression):
            action_text = action_text.text
2235
          obj.addAction(
2236 2237
                        id = action.id
                      , name = action.title
2238
                      , action = action_text
2239 2240 2241 2242
                      , condition = action.getCondition()
                      , permission = action.permissions
                      , category = action.category
                      , visible = action.visible
2243 2244
                      , icon = getattr(action, 'icon', None)\
                                and action.icon.text or ''
2245
                      , priority = action.priority
Aurel's avatar
Aurel committed
2246
                    )
2247
          # sort action based on the priority define on it
2248 2249 2250 2251 2252 2253 2254 2255 2256
          # XXX suppose that priority are properly on actions
          new_priority = action.priority
          action_list = obj.listActions()
          move_down_list = []
          for index in range(len(action_list)):
            action = action_list[index]
            if action.priority > new_priority:
              move_down_list.append(str(index))
          obj.moveDownActions(selections=tuple(move_down_list))
Aurel's avatar
Aurel committed
2257
    else:
2258
      BaseTemplateItem.install(self, context, trashbin, **kw)
Aurel's avatar
Aurel committed
2259
      p = context.getPortalObject()
2260 2261
      for id in self._archive.keys():
        action = self._archive[id]
Aurel's avatar
Aurel committed
2262
        relative_url, key, value = self._splitPath(id)
2263 2264
        obj = p.unrestrictedTraverse(relative_url)
        for ai in obj.listActions():
Aurel's avatar
Aurel committed
2265
          if getattr(ai, key) == value:
2266
            raise TemplateConflictError, 'the portal type %s already has the action %s' % (obj.id, value)
2267 2268 2269
        action_text = action.action
        if isinstance(action_text, Expression):
          action_text = action_text.text
2270
        obj.addAction(
Aurel's avatar
Aurel committed
2271 2272
                      id = action.id
                    , name = action.title
2273
                    , action = action_text
Aurel's avatar
Aurel committed
2274 2275 2276 2277
                    , condition = action.getCondition()
                    , permission = action.permissions
                    , category = action.category
                    , visible = action.visible
2278 2279
                    , icon = getattr(action, 'icon', None) \
                                      and action.icon.text or ''
Aurel's avatar
Aurel committed
2280
                    )
Aurel's avatar
Aurel committed
2281 2282 2283 2284 2285 2286 2287
        new_priority = action.priority
        action_list = obj.listActions()
        move_down_list = []
        for index in range(len(action_list)):
          action = action_list[index]
          if action.priority > new_priority:
            move_down_list.append(str(index))
2288 2289
          obj.moveDownActions(selections=tuple(move_down_list))

2290 2291
  def uninstall(self, context, **kw):
    p = context.getPortalObject()
2292 2293
    object_path = kw.get("object_path", None)
    if object_path is not None:
2294 2295 2296 2297 2298 2299 2300 2301
      if '/' in object_path:
        # here object_path is the path of the actions, something like
        # portal_type/Person/view
        ti, action_id = object_path.rsplit('/', 1)
        keys = ['%s | %s' % (ti, action_id)]
      else:
        # compatibility ?
        keys = [object_path]
2302
    else:
2303
      keys = self._archive.keys()
2304
    for id in keys:
2305 2306 2307 2308
      if  '|' in id:
        relative_url, value = id.split(' | ')
        key = 'id'
      else:
2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319
        relative_url, key, value = self._splitPath(id)
      obj = p.unrestrictedTraverse(relative_url, None)
      if obj is not None:
        action_list = obj.listActions()
        for index in range(len(action_list)):
          if getattr(action_list[index], key) == value:
            obj.deleteActions(selections=(index,))
            break
      else :
        LOG('BusinessTemplate', 100,
            'unable to uninstall action at %s, ignoring' % relative_url )
2320 2321
    BaseTemplateItem.uninstall(self, context, **kw)

2322 2323 2324
class PortalTypeRolesTemplateItem(BaseTemplateItem):

  def __init__(self, id_list, **kw):
2325
    id_list = ['portal_type_roles/%s' % id for id in id_list if id != '']
2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337
    BaseTemplateItem.__init__(self, id_list, **kw)

  def build(self, context, **kw):
    p = context.getPortalObject()
    for relative_url in self._archive.keys():
      obj = p.unrestrictedTraverse("portal_types/%s" %
          relative_url.split('/', 1)[1])
      type_roles_obj = getattr(obj, '_roles', ())
      type_role_list = []
      for role in type_roles_obj:
        type_role_dict = {}
        # uniq
2338
        for property in ('id', 'title', 'description',
2339 2340 2341 2342
            'priority', 'base_category_script'):
          prop_value = getattr(role, property)
          if prop_value:
            type_role_dict[property] = prop_value
2343 2344 2345 2346
        # condition
        prop_value = getattr(role, 'condition')
        if prop_value:
          type_role_dict['condition'] = prop_value.text
2347 2348 2349 2350 2351 2352 2353 2354 2355
        # multi
        for property in ('category', 'base_category'):
          prop_value_list = []
          for prop_value in getattr(role, property):
            prop_value_list.append(prop_value)
          type_role_dict[property] = prop_value_list
        type_role_list.append(type_role_dict)
      self._objects[relative_url] = type_role_list

Christophe Dumez's avatar
Christophe Dumez committed
2356
  # Function to generate XML Code Manually
2357 2358 2359 2360
  def generateXml(self, path=None):
    type_role_list = self._objects[path]
    xml_data = '<type_roles>'
    for role in type_role_list:
2361
      xml_data += "\n  <role id='%s'>" % role['id']
2362 2363 2364 2365 2366
      # uniq
      for property in ('title', 'description', 'condition', 'priority',
          'base_category_script'):
        prop_value = role.get(property)
        if prop_value:
Nicolas Delaby's avatar
Nicolas Delaby committed
2367 2368
          if isinstance(prop_value, str):
            prop_value = prop_value.decode('utf-8')
2369
          xml_data += "\n   <property id='%s'>%s</property>" % \
2370 2371 2372
              (property, prop_value)
      # multi
      for property in ('category', 'base_category'):
2373
        for prop_value in role.get(property, []):
Nicolas Delaby's avatar
Nicolas Delaby committed
2374 2375
          if isinstance(prop_value, str):
            prop_value = prop_value.decode('utf-8')
2376
          xml_data += "\n   <multi_property "\
2377
          "id='%s'>%s</multi_property>" % (property, prop_value)
2378 2379
      xml_data += "\n  </role>"
    xml_data += '\n</type_roles>'
2380
    return xml_data
2381

2382 2383 2384 2385 2386 2387 2388
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=root_path)
    for key in self._objects.keys():
      xml_data = self.generateXml(key)
2389 2390
      if isinstance(xml_data, unicode):
        xml_data = xml_data.encode('utf-8')
2391 2392 2393 2394 2395 2396
      name = key.split('/', 1)[1]
      bta.addObject(obj=xml_data, name=name, path=root_path)

  def _importFile(self, file_name, file):
    type_roles_list = []
    xml = parse(file)
2397
    xml_type_roles_list = xml.getElementsByTagName('role')
2398 2399 2400 2401 2402 2403 2404 2405
    for role in xml_type_roles_list:
      id = role.getAttribute('id')
      type_role_property_dict = {'id':id}
      # uniq
      property_list = role.getElementsByTagName('property')
      for property in property_list:
        property_id = property.getAttribute('id').encode()
        if property.hasChildNodes():
2406
          property_value = property.childNodes[0].data.encode('utf_8', 'backslashreplace')
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416
          if property_id == 'priority':
            property_value = float(property_value)
          type_role_property_dict[property_id] = property_value
      # multi
      multi_property_list = role.getElementsByTagName('multi_property')
      for property in multi_property_list:
        property_id = property.getAttribute('id').encode()
        if not type_role_property_dict.has_key(property_id):
          type_role_property_dict[property_id] = []
        if property.hasChildNodes():
2417
          property_value = property.childNodes[0].data.encode('utf_8', 'backslashreplace')
2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432
          type_role_property_dict[property_id].append(property_value)
      type_roles_list.append(type_role_property_dict)
    self._objects['portal_type_roles/'+file_name[:-4]] = type_roles_list

  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    p = context.getPortalObject()
    for roles_path in self._objects.keys():
      if update_dict.has_key(roles_path) or force:
        if not force:
          action = update_dict[roles_path]
          if action == 'nothing':
            continue
      path = 'portal_types/%s' % roles_path.split('/', 1)[1]
2433 2434 2435 2436 2437 2438
      obj = p.unrestrictedTraverse(path, None)
      if obj is not None:
        setattr(obj, '_roles', []) # reset roles before applying
        type_roles_list = self._objects[roles_path] or []
        for type_role_property_dict in type_roles_list:
          obj._roles.append(RoleInformation(**type_role_property_dict))
2439 2440 2441

  def uninstall(self, context, **kw):
    p = context.getPortalObject()
Aurel's avatar
Aurel committed
2442 2443 2444 2445 2446 2447
    object_path = kw.get('object_path', None)
    if object_path is not None:
      keys = [object_path]
    else:
      keys = self._objects.keys()
    for roles_path in keys:
2448
      path = 'portal_types/%s' % roles_path.split('/', 1)[1]
2449 2450 2451 2452 2453
      try:
        obj = p.unrestrictedTraverse(path)
        setattr(obj, '_roles', [])
      except (NotFound, KeyError):
        pass
2454

2455 2456 2457 2458 2459 2460
class SitePropertyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
    for id in self._archive.keys():
2461 2462
      for property in p.propertyMap():
        if property['id'] == id:
2463
          obj = p.getProperty(id)
2464
          prop_type = property['type']
2465 2466
          break
      else:
2467 2468
        obj = None
      if obj is None:
2469
        raise NotFound, 'the property %s is not found' % id
2470
      self._objects[id] = (prop_type, obj)
Aurel's avatar
Aurel committed
2471

2472 2473 2474 2475 2476 2477
  def _importFile(self, file_name, file):
    # recreate list of site property from xml file
    xml = parse(file)
    property_list = xml.getElementsByTagName('property')
    for prop in property_list:
      id = prop.getElementsByTagName('id')[0].childNodes[0].data
2478 2479
      prop_type = prop.getElementsByTagName('type')[0].childNodes[0].data
      if prop_type in ('lines', 'tokens'):
2480 2481 2482 2483 2484 2485 2486
        value = []
        values = prop.getElementsByTagName('value')[0]
        items = values.getElementsByTagName('item')
        for item in items:
          i = item.childNodes[0].data
          value.append(str(i))
      else:
2487
        value = str(prop.getElementsByTagName('value')[0].childNodes[0].data)
2488
      self._objects[str(id)] = (str(prop_type), value)
2489

2490 2491 2492
  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2493
    if context.getTemplateFormatVersion() == 1:
Aurel's avatar
Aurel committed
2494 2495
      p = context.getPortalObject()
      for path in self._objects.keys():
2496 2497 2498 2499
        if update_dict.has_key(path) or force:
          if not force:
            action = update_dict[path]
            if action == 'nothing':
2500
              continue
2501
          dir, id = posixpath.split(path)
2502 2503
          if p.hasProperty(id):
            continue
2504 2505
          prop_type, property = self._objects[path]
          p._setProperty(id, property, type=prop_type)
Aurel's avatar
Aurel committed
2506
    else:
2507
      BaseTemplateItem.install(self, context, trashbin, **kw)
Aurel's avatar
Aurel committed
2508
      p = context.getPortalObject()
2509 2510
      for id,property in self._archive.keys():
        property = self._archive[id]
Aurel's avatar
Aurel committed
2511 2512 2513 2514 2515
        if p.hasProperty(id):
          continue
          # Too much???
          #raise TemplateConflictError, 'the property %s already exists' % id
        p._setProperty(id, property['value'], type=property['type'])
2516 2517 2518

  def uninstall(self, context, **kw):
    p = context.getPortalObject()
2519 2520 2521 2522 2523 2524
    object_path = kw.get('object_path', None)
    if object_path is not None:
      keys = [object_path]
    else:
      keys = self._archive.keys()
    for id in keys:
2525 2526 2527 2528
      if p.hasProperty(id):
        p._delProperty(id)
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
2529
  # Function to generate XML Code Manually
2530
  def generateXml(self, path=None):
2531
    xml_data = ''
2532
    prop_type, obj = self._objects[path]
2533 2534 2535
    xml_data += '\n <property>'
    xml_data += '\n  <id>%s</id>' %(path,)
    xml_data += '\n  <type>%s</type>' %(prop_type,)
2536
    if prop_type in ('lines', 'tokens'):
2537
      xml_data += '\n  <value>'
2538
      for item in obj:
2539
        if item != '':
2540 2541
          xml_data += '\n   <item>%s</item>' %(item,)
      xml_data += '\n  </value>'
2542
    else:
2543 2544
      xml_data += '\n  <value>%r</value>' %(('\n').join(obj),)
    xml_data += '\n </property>'
2545 2546
    return xml_data

Aurel's avatar
Aurel committed
2547 2548 2549 2550 2551
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=root_path)
2552 2553 2554 2555
    xml_data = '<site_property>'
    keys = self._objects.keys()
    keys.sort()
    for path in keys:
2556
      xml_data += self.generateXml(path)
2557
    xml_data += '\n</site_property>'
2558
    bta.addObject(obj=xml_data, name='properties', path=root_path)
2559

2560 2561 2562 2563 2564 2565 2566
class ModuleTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    p = context.getPortalObject()
    for id in self._archive.keys():
      module = p.unrestrictedTraverse(id)
2567 2568 2569 2570 2571 2572 2573 2574
      dict = {}
      dict['id'] = module.getId()
      dict['title'] = module.getTitle()
      dict['portal_type'] = module.getPortalType()
      permission_list = []
      # use show permission
      dict['permission_list'] = module.showPermissions()
      self._objects[id] = dict
Jean-Paul Smets's avatar
Jean-Paul Smets committed
2575

Christophe Dumez's avatar
Christophe Dumez committed
2576
  # Function to generate XML Code Manually
2577
  def generateXml(self, path=None):
2578 2579
    dict = self._objects[path]
    xml_data = '<module>'
2580 2581 2582 2583
    # sort key
    keys = dict.keys()
    keys.sort()
    for key in keys:
2584 2585
      if key =='permission_list':
        # separe permission dict into xml
2586
        xml_data += '\n <%s>' %(key,)
2587 2588
        permission_list = dict[key]
        for perm in permission_list:
2589 2590 2591 2592 2593
          # the type of the permission defined if we use acquired or not
          if type(perm[1]) == type([]):
            ptype = "list"
          else:
            ptype = "tuple"
2594 2595
          xml_data += "\n  <permission type='%s'>" %(ptype,)
          xml_data += '\n   <name>%s</name>' %(perm[0])
Aurel's avatar
Aurel committed
2596
          role_list = list(perm[1])
2597
          role_list.sort()
2598
          for role in role_list:
2599 2600 2601
            xml_data += '\n   <role>%s</role>' %(role)
          xml_data += '\n  </permission>'
        xml_data += '\n </%s>' %(key,)
2602
      else:
2603 2604
        xml_data += '\n <%s>%s</%s>' %(key, dict[key], key)
    xml_data += '\n</module>'
2605 2606
    return xml_data

2607 2608 2609 2610 2611
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(path)
2612 2613 2614
    keys = self._objects.keys()
    keys.sort()
    for id in keys:
2615
      # expor module one by one
2616
      xml_data = self.generateXml(path=id)
2617
      bta.addObject(obj=xml_data, name=id, path=path)
2618

2619
  def install(self, context, trashbin, **kw):
2620
    portal = context.getPortalObject()
2621 2622
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2623
    if context.getTemplateFormatVersion() == 1:
2624
      items = self._objects
2625
    else:
2626 2627 2628 2629 2630 2631 2632 2633 2634
      items = self._archive

    for id in items.keys():
      if update_dict.has_key(id) or force:
        if not force:
          action = update_dict[id]
          if action == 'nothing':
            continue
        mapping = items[id]
2635
        path, id = posixpath.split(id)
2636 2637
        if id in portal.objectIds():
          module = portal._getOb(id)
2638
          module.portal_type = str(mapping['portal_type'])
2639 2640 2641 2642 2643 2644 2645 2646 2647 2648
        else:
          module = portal.newContent(id=id, portal_type=str(mapping['portal_type']))
        module.setTitle(str(mapping['title']))
        for name,role_list in list(mapping['permission_list']):
          acquire = (type(role_list) == type([]))
          try:
            module.manage_permission(name, roles=role_list, acquire=acquire)
          except ValueError:
            # Ignore a permission not present in this system.
            pass
2649 2650 2651 2652 2653 2654 2655 2656 2657 2658

  def _importFile(self, file_name, file):
    dict = {}
    xml = parse(file)
    for id in ('portal_type', 'id', 'title', 'permission_list'):
      elt = xml.getElementsByTagName(id)[0]
      if id == 'permission_list':
        plist = []
        perm_list = elt.getElementsByTagName('permission')
        for perm in perm_list:
2659
          perm_type = perm.getAttribute('type').encode() or None
2660 2661 2662 2663 2664 2665 2666 2667 2668
          name_elt = perm.getElementsByTagName('name')[0]
          name_node = name_elt.childNodes[0]
          name = name_node.data
          role_list = perm.getElementsByTagName('role')
          rlist = []
          for role in role_list:
            role_node = role.childNodes[0]
            role = role_node.data
            rlist.append(str(role))
2669 2670 2671 2672
          if perm_type == "list" or perm_type is None:
            perm_tuple = (str(name), list(rlist))
          else:
            perm_tuple = (str(name), tuple(rlist))
2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683
          plist.append(perm_tuple)
        dict[id] = plist
      else:
        node_list = elt.childNodes
        if len(node_list) == 0:
          value=''
        else:
          value = node_list[0].data
        dict[id] = str(value)
    self._objects[file_name[:-4]] = dict

2684
  def uninstall(self, context, **kw):
Aurel's avatar
Aurel committed
2685 2686 2687
    trash = kw.get('trash', 0)
    if trash:
      return
2688 2689 2690 2691 2692 2693
    object_path = kw.get('object_path', None)
    trashbin = kw.get('trashbin', None)
    if object_path is None:
      keys = self._archive.keys()
    else:
      keys = [object_path]
2694 2695
    p = context.getPortalObject()
    id_list = p.objectIds()
2696
    for id in keys:
2697
      if id in id_list:
2698
        try:
2699
          if trash and trashbin is not None:
2700 2701
            container_path = id.split('/')
            self.portal_trash.backupObject(trashbin, container_path, id, save=1, keep_subobjects=1)
2702
          p.manage_delObjects([id])
2703
        except NotFound:
2704
          pass
2705 2706
    BaseTemplateItem.uninstall(self, context, **kw)

2707 2708 2709
  def trash(self, context, new_item, **kw):
    # Do not remove any module for safety.
    pass
2710 2711

class DocumentTemplateItem(BaseTemplateItem):
2712 2713 2714 2715
  local_file_reader_name = 'readLocalDocument'
  local_file_writer_name = 'writeLocalDocument'
  local_file_importer_name = 'importLocalDocument'
  local_file_remover_name = 'removeLocalDocument'
2716 2717 2718 2719

  def build(self, context, **kw):
    BaseTemplateItem.build(self, context, **kw)
    for id in self._archive.keys():
2720
      self._objects[self.__class__.__name__+'/'+id] = globals()[self.local_file_reader_name](id)
Aurel's avatar
Aurel committed
2721

2722
  def preinstall(self, context, installed_bt, **kw):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2723 2724
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
2725
      new_keys = self._objects.keys()
2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
      new_dict = PersistentMapping()
      # fix key if necessary in installed bt for diff
      for key in installed_bt._objects.keys():
        if self.__class__.__name__ in key:
          new_key = key[len('%s/' % self.__class__.__name__):]
          new_dict[new_key] = installed_bt._objects[key]
        else:
          new_dict[key] = installed_bt._objects[key]
      if len(new_dict):
        installed_bt._objects = new_dict
2736 2737 2738 2739 2740 2741
      for path in new_keys:
        if installed_bt._objects.has_key(path):
          # compare object to see if there is changes
          new_obj_code = self._objects[path]
          old_obj_code = installed_bt._objects[path]
          if new_obj_code != old_obj_code:
2742 2743
            modified_object_list.update(
                {path : ['Modified', self.__class__.__name__[:-12]]})
2744
        else: # new object
2745 2746
          modified_object_list.update(
                {path : ['New', self.__class__.__name__[:-12]]})
2747 2748 2749 2750
          # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
2751 2752
          modified_object_list.update(
                {path : ['Removed', self.__class__.__name__[:-12]]})
2753 2754 2755 2756 2757
    return modified_object_list

  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2758
    if context.getTemplateFormatVersion() == 1:
Aurel's avatar
Aurel committed
2759
      for id in self._objects.keys():
2760 2761 2762 2763 2764 2765
        if update_dict.has_key(id) or force:
          if not force:
            action = update_dict[id]
            if action == 'nothing':
              continue
          text = self._objects[id]
2766
          path, name = posixpath.split(id)
2767 2768
          # This raises an exception if the file already exists.
          try:
2769
            globals()[self.local_file_writer_name](name, text, create=0)
2770
          except IOError, error:
2771
            LOG("BusinessTemplate.py", WARNING, "Cannot install class %s on file system" %(name,))
2772 2773
            if error.errno :
              raise
2774 2775 2776
            continue
          if self.local_file_importer_name is not None:
            globals()[self.local_file_importer_name](name)
Aurel's avatar
Aurel committed
2777
    else:
2778 2779 2780
      BaseTemplateItem.install(self, context, trashbin, **kw)
      for id in self._archive.keys():
        text = self._archive[id]
Aurel's avatar
Aurel committed
2781
        # This raises an exception if the file exists.
2782
        globals()[self.local_file_writer_name](id, text, create=1)
Aurel's avatar
Aurel committed
2783 2784
        if self.local_file_importer_name is not None:
          globals()[self.local_file_importer_name](id)
2785 2786

  def uninstall(self, context, **kw):
2787
    object_path = kw.get('object_path', None)
2788 2789 2790 2791 2792
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for id in object_keys:
2793
      globals()[self.local_file_remover_name](id)
2794 2795
    BaseTemplateItem.uninstall(self, context, **kw)

Aurel's avatar
Aurel committed
2796 2797 2798 2799 2800 2801
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
2802 2803
      obj = self._objects[path]
      bta.addObject(obj=obj, name=path, path=None, ext='.py')
Aurel's avatar
Aurel committed
2804

2805
  def _importFile(self, file_name, file):
2806 2807 2808
    if not file_name.endswith('.py'):
      LOG('Business Template', 0, 'Skipping file "%s"' % (file_name, ))
      return
2809
    text = file.read()
2810
    self._objects[file_name[:-3]]=text
2811

2812 2813 2814 2815 2816 2817
class PropertySheetTemplateItem(DocumentTemplateItem):
  local_file_reader_name = 'readLocalPropertySheet'
  local_file_writer_name = 'writeLocalPropertySheet'
  local_file_importer_name = 'importLocalPropertySheet'
  local_file_remover_name = 'removeLocalPropertySheet'

2818 2819 2820 2821 2822
class ConstraintTemplateItem(DocumentTemplateItem):
  local_file_reader_name = 'readLocalConstraint'
  local_file_writer_name = 'writeLocalConstraint'
  local_file_importer_name = 'importLocalConstraint'
  local_file_remover_name = 'removeLocalConstraint'
2823

2824 2825 2826
class ExtensionTemplateItem(DocumentTemplateItem):
  local_file_reader_name = 'readLocalExtension'
  local_file_writer_name = 'writeLocalExtension'
2827 2828
  # Extension needs no import
  local_file_importer_name = None
2829 2830 2831 2832 2833
  local_file_remover_name = 'removeLocalExtension'

class TestTemplateItem(DocumentTemplateItem):
  local_file_reader_name = 'readLocalTest'
  local_file_writer_name = 'writeLocalTest'
2834
  # Test needs no import
2835 2836 2837
  local_file_importer_name = None
  local_file_remover_name = 'removeLocalTest'

Aurel's avatar
Aurel committed
2838

2839 2840 2841
class ProductTemplateItem(BaseTemplateItem):
  # XXX Not implemented yet
  pass
2842 2843 2844

class RoleTemplateItem(BaseTemplateItem):

Aurel's avatar
Aurel committed
2845 2846 2847 2848
  def build(self, context, **kw):
    role_list = []
    for key in self._archive.keys():
      role_list.append(key)
2849
    if len(role_list) > 0:
2850
      self._objects[self.__class__.__name__+'/'+'role_list'] = role_list
Aurel's avatar
Aurel committed
2851

2852
  def preinstall(self, context, installed_bt, **kw):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2853 2854
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
2855
      new_roles = self._objects.keys()
2856
      if installed_bt.id == INSTALLED_BT_FOR_DIFF:
2857 2858
        #must rename keys in dict if reinstall
        new_dict = PersistentMapping()
Aurel's avatar
Aurel committed
2859 2860 2861
        old_keys = ()
        if len(installed_bt._objects.values()) > 0:
          old_keys = installed_bt._objects.values()[0]
2862 2863
        for key in old_keys:
          new_dict[key] = ''
2864
        installed_bt._objects = new_dict
2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877
      for role in new_roles:
        if installed_bt._objects.has_key(role):
          continue
        else: # only show new roles
          modified_object_list.update({role : ['New', 'Role']})
      # get removed roles
      old_roles = installed_bt._objects.keys()
      for role in old_roles:
        if role not in new_roles:
          modified_object_list.update({role : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

  def install(self, context, trashbin, **kw):
2878
    p = context.getPortalObject()
2879
    # get roles
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2880
    if context.getTemplateFormatVersion() == 1:
2881
      role_list = self._objects.keys()
2882
    else:
2883
      role_list = self._archive.keys()
2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895
    # set roles in PAS
    if p.acl_users.meta_type == 'Pluggable Auth Service':
      role_manager_list = p.acl_users.objectValues('ZODB Role Manager')
      for role_manager in role_manager_list:
        existing_role_list = role_manager.listRoleIds()
        for role in role_list:
          if role not in existing_role_list:
            role_manager.addRole(role)
    # set roles on portal
    roles = {}
    for role in p.__ac_roles__:
      roles[role] = 1
2896
    for role in role_list:
2897 2898 2899 2900 2901 2902 2903 2904 2905 2906
      roles[role] = 1
    p.__ac_roles__ = tuple(roles.keys())

  def _importFile(self, file_name, file):
    xml = parse(file)
    role_list = xml.getElementsByTagName('role')
    for role in role_list:
      node = role.childNodes[0]
      value = node.data
      self._objects[str(value)] = 1
2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918

  def uninstall(self, context, **kw):
    p = context.getPortalObject()
    roles = {}
    for role in p.__ac_roles__:
      roles[role] = 1
    for role in self._archive.keys():
      if role in roles:
        del roles[role]
    p.__ac_roles__ = tuple(roles.keys())
    BaseTemplateItem.uninstall(self, context, **kw)

2919 2920 2921 2922 2923 2924 2925 2926 2927
  def trash(self, context, new_item, **kw):
    p = context.getPortalObject()
    new_roles = {}
    for role in new_item._archive.keys():
      new_roles[role] = 1
    roles = {}
    for role in p.__ac_roles__:
      roles[role] = 1
    for role in self._archive.keys():
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2928
      if role in roles and role not in new_roles:
2929 2930 2931
        del roles[role]
    p.__ac_roles__ = tuple(roles.keys())

Christophe Dumez's avatar
Christophe Dumez committed
2932
  # Function to generate XML Code Manually
2933
  def generateXml(self, path):
2934
    obj = self._objects[path]
2935
    xml_data = '<role_list>'
2936 2937
    obj.sort()
    for role in obj:
2938 2939
      xml_data += '\n <role>%s</role>' %(role)
    xml_data += '\n</role_list>'
2940 2941
    return xml_data

Aurel's avatar
Aurel committed
2942 2943 2944 2945 2946 2947
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
2948
      xml_data = self.generateXml(path=path)
2949
      bta.addObject(obj=xml_data, name=path, path=None,)
2950

2951 2952
class CatalogResultKeyTemplateItem(BaseTemplateItem):

Aurel's avatar
Aurel committed
2953
  def build(self, context, **kw):
2954
    catalog = _getCatalogValue(self)
2955 2956 2957
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
2958
    sql_search_result_keys = list(catalog.sql_search_result_keys)
2959
    key_list = []
2960
    for key in self._archive.keys():
Aurel's avatar
Aurel committed
2961
      if key in sql_search_result_keys:
2962
        key_list.append(key)
Aurel's avatar
Aurel committed
2963
      else:
2964
        raise NotFound, 'Result key "%r" not found in catalog' %(key,)
2965
    if len(key_list) > 0:
2966
      self._objects[self.__class__.__name__+'/'+'result_key_list'] = key_list
2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

2978
  def install(self, context, trashbin, **kw):
2979
    catalog = _getCatalogValue(self)
2980 2981 2982
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
2983

2984
    sql_search_result_keys = list(catalog.sql_search_result_keys)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
2985
    if context.getTemplateFormatVersion() == 1:
2986 2987
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
2988 2989 2990
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
Aurel's avatar
Aurel committed
2991
    else:
2992
      keys = self._archive.keys()
2993 2994
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
2995
    # XXX same as related key
Aurel's avatar
Aurel committed
2996
    if update_dict.has_key('result_key_list') or force:
2997
      if not force:
Aurel's avatar
Aurel committed
2998
        action = update_dict['result_key_list']
2999
        if action == 'nothing':
Aurel's avatar
Aurel committed
3000
          return
3001
      for key in keys:
3002 3003
        if key not in sql_search_result_keys:
          sql_search_result_keys.append(key)
3004
      catalog.sql_search_result_keys = sql_search_result_keys
3005

3006
  def uninstall(self, context, **kw):
3007
    catalog = _getCatalogValue(self)
3008 3009 3010 3011
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_search_result_keys = list(catalog.sql_search_result_keys)
3012
    object_path = kw.get('object_path', None)
3013 3014 3015 3016 3017
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
3018 3019
      if key in sql_search_result_keys:
        sql_search_result_keys.remove(key)
3020
    catalog.sql_search_result_keys = sql_search_result_keys
3021 3022
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3023
  # Function to generate XML Code Manually
3024
  def generateXml(self, path=None):
3025
    obj = self._objects[path]
3026
    xml_data = '<key_list>'
3027 3028
    obj.sort()
    for key in obj:
3029 3030
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3031 3032
    return xml_data

Aurel's avatar
Aurel committed
3033 3034 3035 3036 3037
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
3038
    for path in self._objects.keys():
3039
      xml_data = self.generateXml(path=path)
3040
      bta.addObject(obj=xml_data, name=path, path=None)
3041

3042 3043
class CatalogRelatedKeyTemplateItem(BaseTemplateItem):

Aurel's avatar
Aurel committed
3044
  def build(self, context, **kw):
3045
    catalog = _getCatalogValue(self)
3046 3047 3048
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
Aurel's avatar
Aurel committed
3049
    sql_search_related_keys = list(catalog.sql_catalog_related_keys)
3050
    key_list = []
3051
    for key in self._archive.keys():
Aurel's avatar
Aurel committed
3052
      if key in sql_search_related_keys:
3053
        key_list.append(key)
Aurel's avatar
Aurel committed
3054
      else:
3055
        raise NotFound, 'Related key "%r" not found in catalog' %(key,)
3056
    if len(key_list) > 0:
3057
      self._objects[self.__class__.__name__+'/'+'related_key_list'] = key_list
3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

3069
  def install(self, context, trashbin, **kw):
3070
    catalog = _getCatalogValue(self)
3071 3072 3073
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
3074

3075
    sql_catalog_related_keys = list(catalog.sql_catalog_related_keys)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
3076
    if context.getTemplateFormatVersion() == 1:
3077
      if len(self._objects.keys()) == 0: # needed because of pop()
Aurel's avatar
Aurel committed
3078
        return
3079 3080 3081
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
Aurel's avatar
Aurel committed
3082
    else:
3083
      keys = self._archive.keys()
3084 3085
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
Aurel's avatar
Aurel committed
3086
    # XXX must a find a better way to manage related key
Aurel's avatar
Aurel committed
3087
    if update_dict.has_key('related_key_list') or update_dict.has_key('key_list') or force:
Aurel's avatar
Aurel committed
3088
      if not force:
Aurel's avatar
Aurel committed
3089 3090 3091 3092
        if update_dict.has_key('related_key_list'):
          action = update_dict['related_key_list']
        else: # XXX for backward compatibility
          action = update_dict['key_list']
Aurel's avatar
Aurel committed
3093 3094 3095
        if action == 'nothing':
          return
      for key in keys:
3096 3097
        if key not in sql_catalog_related_keys:
          sql_catalog_related_keys.append(key)
Aurel's avatar
Aurel committed
3098
      catalog.sql_catalog_related_keys = tuple(sql_catalog_related_keys)
3099

3100
  def uninstall(self, context, **kw):
3101
    catalog = _getCatalogValue(self)
3102 3103 3104
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
3105
    sql_catalog_related_keys = list(catalog.sql_catalog_related_keys)
3106
    object_path = kw.get('object_path', None)
3107 3108 3109 3110 3111
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
3112 3113 3114 3115 3116
      if key in sql_catalog_related_keys:
        sql_catalog_related_keys.remove(key)
    catalog.sql_catalog_related_keys = sql_catalog_related_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3117
  # Function to generate XML Code Manually
3118
  def generateXml(self, path=None):
3119
    obj = self._objects[path]
3120
    xml_data = '<key_list>'
3121 3122
    obj.sort()
    for key in obj:
3123 3124
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3125 3126
    return xml_data

Aurel's avatar
Aurel committed
3127 3128 3129 3130 3131 3132
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
3133
      xml_data = self.generateXml(path=path)
3134
      bta.addObject(obj=xml_data, name=path, path=None)
3135

3136 3137
class CatalogResultTableTemplateItem(BaseTemplateItem):

Aurel's avatar
Aurel committed
3138
  def build(self, context, **kw):
3139
    catalog = _getCatalogValue(self)
3140 3141 3142
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
Aurel's avatar
Aurel committed
3143
    sql_search_result_tables = list(catalog.sql_search_tables)
3144
    key_list = []
3145
    for key in self._archive.keys():
Aurel's avatar
Aurel committed
3146
      if key in sql_search_result_tables:
3147
        key_list.append(key)
Aurel's avatar
Aurel committed
3148
      else:
3149
        raise NotFound, 'Result table "%r" not found in catalog' %(key,)
3150
    if len(key_list) > 0:
3151
      self._objects[self.__class__.__name__+'/'+'result_table_list'] = key_list
3152 3153 3154 3155 3156 3157 3158 3159 3160 3161

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list
Aurel's avatar
Aurel committed
3162

3163
  def install(self, context, trashbin, **kw):
3164
    catalog = _getCatalogValue(self)
3165 3166 3167
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
3168

3169
    sql_search_tables = list(catalog.sql_search_tables)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
3170
    if context.getTemplateFormatVersion() == 1:
3171
      if len(self._objects.keys()) == 0: # needed because of pop()
Aurel's avatar
Aurel committed
3172
        return
3173 3174 3175
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
Aurel's avatar
Aurel committed
3176
    else:
3177
      keys = self._archive.keys()
3178 3179
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
3180
    # XXX same as related keys
Aurel's avatar
Aurel committed
3181
    if update_dict.has_key('result_table_list') or force:
3182
      if not force:
Aurel's avatar
Aurel committed
3183
        action = update_dict['result_table_list']
3184
        if action == 'nothing':
Aurel's avatar
Aurel committed
3185
          return
3186
      for key in keys:
3187 3188
        if key not in sql_search_tables:
          sql_search_tables.append(key)
3189
      catalog.sql_search_tables = tuple(sql_search_tables)
3190 3191

  def uninstall(self, context, **kw):
3192
    catalog = _getCatalogValue(self)
3193 3194 3195 3196
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_search_tables = list(catalog.sql_search_tables)
3197
    object_path = kw.get('object_path', None)
3198 3199 3200 3201 3202
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
3203 3204
      if key in sql_search_tables:
        sql_search_tables.remove(key)
3205
    catalog.sql_search_tables = sql_search_tables
3206 3207
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3208
  # Function to generate XML Code Manually
3209
  def generateXml(self, path=None):
3210
    obj = self._objects[path]
3211
    xml_data = '<key_list>'
3212 3213
    obj.sort()
    for key in obj:
3214 3215
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3216 3217
    return xml_data

Aurel's avatar
Aurel committed
3218 3219 3220 3221 3222 3223
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
3224
      xml_data = self.generateXml(path=path)
3225
      bta.addObject(obj=xml_data, name=path, path=None)
3226

3227 3228
# keyword
class CatalogKeywordKeyTemplateItem(BaseTemplateItem):
3229 3230

  def build(self, context, **kw):
3231
    catalog = _getCatalogValue(self)
3232 3233 3234 3235 3236 3237 3238 3239 3240
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_keyword_keys = list(catalog.sql_catalog_keyword_search_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_keyword_keys:
        key_list.append(key)
      else:
3241
        raise NotFound, 'Keyword key "%r" not found in catalog' %(key,)
3242
    if len(key_list) > 0:
3243
      self._objects[self.__class__.__name__+'/'+'keyword_key_list'] = key_list
Aurel's avatar
Aurel committed
3244

3245 3246 3247 3248 3249 3250 3251 3252 3253
  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list
3254 3255

  def install(self, context, trashbin, **kw):
3256
    catalog = _getCatalogValue(self)
3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_keyword_keys = list(catalog.sql_catalog_keyword_search_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
3270 3271
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283
    # XXX same as related key
    if update_dict.has_key('keyword_key_list') or force:
      if not force:
        action = update_dict['keyword_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_keyword_keys:
          sql_keyword_keys.append(key)
      catalog.sql_catalog_keyword_search_keys = sql_keyword_keys

  def uninstall(self, context, **kw):
3284
    catalog = _getCatalogValue(self)
3285 3286 3287 3288
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_keyword_keys = list(catalog.sql_catalog_keyword_search_keys)
3289
    object_path = kw.get('object_path', None)
3290 3291
    if object_path is not None:
      object_keys = [object_path]
Aurel's avatar
Aurel committed
3292
    else:
3293 3294 3295 3296 3297 3298 3299
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_keyword_keys:
        sql_keyword_keys.remove(key)
    catalog.sql_catalog_keyword_search_keys = sql_keyword_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3300
  # Function to generate XML Code Manually
3301 3302 3303 3304 3305
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
3306 3307
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3308
    return xml_data
Aurel's avatar
Aurel committed
3309 3310 3311 3312

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
3313 3314 3315 3316 3317
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)
3318

3319 3320 3321 3322 3323 3324 3325 3326
# datetime
class CatalogDateTimeKeyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
3327
    sql_datetime_keys = list(getattr(catalog, 'sql_catalog_datetime_search_keys', []))
3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352
    key_list = []
    for key in self._archive.keys():
      if key in sql_datetime_keys:
        key_list.append(key)
      else:
        raise NotFound, 'DateTime key "%r" not found in catalog' %(key,)
    if len(key_list) > 0:
      self._objects[self.__class__.__name__+'/'+'datetime_key_list'] = key_list

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
    catalog = _getCatalogValue(context)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

3353
    sql_datetime_keys = list(getattr(catalog, 'sql_catalog_datetime_search_keys', []))
3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX same as related key
    if update_dict.has_key('datetime_key_list') or force:
      if not force:
        action = update_dict['datetime_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_datetime_keys:
          sql_datetime_keys.append(key)
      catalog.sql_catalog_datetime_search_keys = sql_datetime_keys

  def uninstall(self, context, **kw):
    catalog = _getCatalogValue(context)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available - uninstall')
      return
3380
    sql_datetime_keys = list(getattr(catalog, 'sql_catalog_datetime_search_keys', []))
3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_datetime_keys:
        sql_datetime_keys.remove(key)
    catalog.sql_catalog_datetime_search_keys = sql_datetime_keys
    BaseTemplateItem.uninstall(self, context, **kw)

  # Function to generate XML Code Manually
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)      
      
3411 3412
# full text
class CatalogFullTextKeyTemplateItem(BaseTemplateItem):
3413

3414
  def build(self, context, **kw):
3415
    catalog = _getCatalogValue(self)
3416 3417 3418 3419 3420 3421 3422 3423 3424
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_full_text_keys = list(catalog.sql_catalog_full_text_search_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_full_text_keys:
        key_list.append(key)
      else:
3425
        raise NotFound, 'Fulltext key "%r" not found in catalog' %(key,)
3426
    if len(key_list) > 0:
3427
      self._objects[self.__class__.__name__+'/'+'full_text_key_list'] = key_list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
3428

3429 3430 3431 3432 3433 3434 3435 3436 3437
  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
3438

3439
  def install(self, context, trashbin, **kw):
3440
    catalog = _getCatalogValue(self)
3441 3442 3443
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
Jean-Paul Smets's avatar
Jean-Paul Smets committed
3444

3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465
    sql_full_text_keys = list(catalog.sql_catalog_full_text_search_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX same as related key
    if update_dict.has_key('full_text_key_list') or force:
      if not force:
        action = update_dict['full_text_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_full_text_keys:
          sql_full_text_keys.append(key)
      catalog.sql_catalog_full_text_search_keys = sql_full_text_keys
Jean-Paul Smets's avatar
Jean-Paul Smets committed
3466

3467
  def uninstall(self, context, **kw):
3468
    catalog = _getCatalogValue(self)
3469 3470 3471 3472
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_full_text_keys = list(catalog.sql_catalog_full_text_search_keys)
3473
    object_path = kw.get('object_path', None)
3474 3475 3476 3477 3478 3479 3480 3481 3482 3483
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_full_text_keys:
        sql_full_text_keys.remove(key)
    catalog.sql_catalog_full_text_search_keys = sql_full_text_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3484
  # Function to generate XML Code Manually
3485 3486 3487 3488 3489
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
3490 3491
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)


# request
class CatalogRequestKeyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
3508
    catalog = _getCatalogValue(self)
3509 3510 3511 3512 3513 3514 3515 3516 3517
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_request_keys = list(catalog.sql_catalog_request_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_request_keys:
        key_list.append(key)
      else:
3518
        raise NotFound, 'Request key "%r" not found in catalog' %(key,)
3519
    if len(key_list) > 0:
3520
      self._objects[self.__class__.__name__+'/'+'request_key_list'] = key_list
3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
3533
    catalog = _getCatalogValue(self)
3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_request_keys = list(catalog.sql_catalog_request_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX must a find a better way to manage related key
    if update_dict.has_key('request_key_list') or force:
      if not force:
        action = update_dict['request_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_request_keys:
          sql_catalog_request_keys.append(key)
      catalog.sql_catalog_request_keys = tuple(sql_catalog_request_keys)

  def uninstall(self, context, **kw):
3561
    catalog = _getCatalogValue(self)
3562 3563 3564 3565
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_request_keys = list(catalog.sql_catalog_request_keys)
3566
    object_path = kw.get('object_path', None)
3567 3568 3569 3570 3571 3572 3573 3574 3575 3576
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_request_keys:
        sql_catalog_request_keys.remove(key)
    catalog.sql_catalog_request_keys = sql_catalog_request_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3577
  # Function to generate XML Code Manually
3578 3579 3580 3581 3582
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
3583 3584
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

# multivalue
class CatalogMultivalueKeyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
3600
    catalog = _getCatalogValue(self)
3601 3602 3603 3604 3605 3606 3607 3608 3609
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_multivalue_keys = list(catalog.sql_catalog_multivalue_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_multivalue_keys:
        key_list.append(key)
      else:
3610
        raise NotFound, 'Multivalue key "%r" not found in catalog' %(key,)
3611
    if len(key_list) > 0:
3612
      self._objects[self.__class__.__name__+'/'+'multivalue_key_list'] = key_list
3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
3625
    catalog = _getCatalogValue(self)
3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_multivalue_keys = list(catalog.sql_catalog_multivalue_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    if update_dict.has_key('multivalue_key_list') or force:
      if not force:
        action = update_dict['multivalue_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_multivalue_keys:
          sql_catalog_multivalue_keys.append(key)
      catalog.sql_catalog_multivalue_keys = tuple(sql_catalog_multivalue_keys)

  def uninstall(self, context, **kw):
3652
    catalog = _getCatalogValue(self)
3653 3654 3655 3656
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_multivalue_keys = list(catalog.sql_catalog_multivalue_keys)
3657
    object_path = kw.get('object_path', None)
3658 3659 3660 3661 3662 3663 3664 3665 3666 3667
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_multivalue_keys:
        sql_catalog_multivalue_keys.remove(key)
    catalog.sql_catalog_multivalue_keys = sql_catalog_multivalue_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3668
  # Function to generate XML Code Manually
3669 3670 3671 3672 3673
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
3674 3675
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

# topic
class CatalogTopicKeyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
3691
    catalog = _getCatalogValue(self)
3692 3693 3694 3695 3696 3697 3698 3699 3700
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_topic_search_keys = list(catalog.sql_catalog_topic_search_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_catalog_topic_search_keys:
        key_list.append(key)
      else:
3701
        raise NotFound, 'Topic key "%r" not found in catalog' %(key,)
3702
    if len(key_list) > 0:
3703
      self._objects[self.__class__.__name__+'/'+'topic_key_list'] = key_list
3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
3716
    catalog = _getCatalogValue(self)
3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_topic_search_keys = list(catalog.sql_catalog_topic_search_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX same as related key
    if update_dict.has_key('topic_key_list') or force:
      if not force:
        action = update_dict['topic_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_topic_search_keys:
          sql_catalog_topic_search_keys.append(key)
      catalog.sql_catalog_topic_search_keys = sql_catalog_topic_search_keys

  def uninstall(self, context, **kw):
3744
    catalog = _getCatalogValue(self)
3745 3746 3747 3748
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_topic_search_keys = list(catalog.sql_catalog_topic_search_keys)
3749
    object_path = kw.get('object_path', None)
3750 3751 3752 3753 3754 3755 3756 3757 3758 3759
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_topic_search_keys:
        sql_catalog_topic_search_keys.remove(key)
    catalog.sql_catalog_topic_search_keys = sql_catalog_topic_search_keys
    BaseTemplateItem.uninstall(self, context, **kw)

Christophe Dumez's avatar
Christophe Dumez committed
3760
  # Function to generate XML Code Manually
3761 3762 3763 3764 3765
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
3766 3767
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870
class CatalogScriptableKeyTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_scriptable_keys = list(catalog.sql_catalog_scriptable_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_catalog_scriptable_keys:
        key_list.append(key)
      else:
        raise NotFound, 'Scriptable key "%r" not found in catalog' %(key,)
    if len(key_list) > 0:
      self._objects[self.__class__.__name__+'/'+'scriptable_key_list'] = key_list

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_scriptable_keys = list(catalog.sql_catalog_scriptable_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX must a find a better way to manage scriptable key
    if update_dict.has_key('scriptable_key_list') or force:
      if not force:
        if update_dict.has_key('scriptable_key_list'):
          action = update_dict['scriptable_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_scriptable_keys:
          sql_catalog_scriptable_keys.append(key)
      catalog.sql_catalog_scriptable_keys = tuple(sql_catalog_scriptable_keys)

  def uninstall(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_scriptable_keys = list(catalog.sql_catalog_scriptable_keys)
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_scriptable_keys:
        sql_catalog_scriptable_keys.remove(key)
    catalog.sql_catalog_scriptable_keys = tuple(sql_catalog_scriptable_keys)
    BaseTemplateItem.uninstall(self, context, **kw)

  # Function to generate XML Code Manually
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056
class CatalogRoleKeyTemplateItem(BaseTemplateItem):
  # XXX Copy/paste from CatalogScriptableKeyTemplateItem

  def build(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_role_keys = list(catalog.sql_catalog_role_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_catalog_role_keys:
        key_list.append(key)
      else:
        raise NotFound, 'Role key "%r" not found in catalog' %(key,)
    if len(key_list) > 0:
      self._objects[self.__class__.__name__+'/'+'role_key_list'] = key_list

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_role_keys = list(catalog.sql_catalog_role_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX must a find a better way to manage scriptable key
    if update_dict.has_key('role_key_list') or force:
      if not force:
        if update_dict.has_key('role_key_list'):
          action = update_dict['role_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_role_keys:
          sql_catalog_role_keys.append(key)
      catalog.sql_catalog_role_keys = tuple(sql_catalog_role_keys)

  def uninstall(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_role_keys = list(catalog.sql_catalog_role_keys)
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_role_keys:
        sql_catalog_role_keys.remove(key)
    catalog.sql_catalog_role_keys = tuple(sql_catalog_role_keys)
    BaseTemplateItem.uninstall(self, context, **kw)

  # Function to generate XML Code Manually
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

class CatalogLocalRoleKeyTemplateItem(BaseTemplateItem):
  # XXX Copy/paste from CatalogScriptableKeyTemplateItem

  def build(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_local_role_keys = list(catalog.sql_catalog_local_role_keys)
    key_list = []
    for key in self._archive.keys():
      if key in sql_catalog_local_role_keys:
        key_list.append(key)
      else:
        raise NotFound, 'LocalRole key "%r" not found in catalog' %(key,)
    if len(key_list) > 0:
      self._objects[self.__class__.__name__+'/'+'local_role_key_list'] = key_list

  def _importFile(self, file_name, file):
    list = []
    xml = parse(file)
    key_list = xml.getElementsByTagName('key')
    for key in key_list:
      node = key.childNodes[0]
      value = node.data
      list.append(str(value))
    self._objects[file_name[:-4]] = list

  def install(self, context, trashbin, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return

    sql_catalog_local_role_keys = list(catalog.sql_catalog_local_role_keys)
    if context.getTemplateFormatVersion() == 1:
      if len(self._objects.keys()) == 0: # needed because of pop()
        return
      keys = []
      for k in self._objects.values().pop(): # because of list of list
        keys.append(k)
    else:
      keys = self._archive.keys()
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    # XXX must a find a better way to manage scriptable key
    if update_dict.has_key('local_role_key_list') or force:
      if not force:
        if update_dict.has_key('local_role_key_list'):
          action = update_dict['local_role_key_list']
        if action == 'nothing':
          return
      for key in keys:
        if key not in sql_catalog_local_role_keys:
          sql_catalog_local_role_keys.append(key)
      catalog.sql_catalog_local_role_keys = tuple(sql_catalog_local_role_keys)

  def uninstall(self, context, **kw):
    catalog = _getCatalogValue(self)
    if catalog is None:
      LOG('BusinessTemplate', 0, 'no SQL catalog was available')
      return
    sql_catalog_local_role_keys = list(catalog.sql_catalog_local_role_keys)
    object_path = kw.get('object_path', None)
    if object_path is not None:
      object_keys = [object_path]
    else:
      object_keys = self._archive.keys()
    for key in object_keys:
      if key in sql_catalog_local_role_keys:
        sql_catalog_local_role_keys.remove(key)
    catalog.sql_catalog_local_role_keys = tuple(sql_catalog_local_role_keys)
    BaseTemplateItem.uninstall(self, context, **kw)

  # Function to generate XML Code Manually
  def generateXml(self, path=None):
    obj = self._objects[path]
    xml_data = '<key_list>'
    obj.sort()
    for key in obj:
      xml_data += '\n <key>%s</key>' %(key)
    xml_data += '\n</key_list>'
    return xml_data

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=path)
    for path in self._objects.keys():
      xml_data = self.generateXml(path=path)
      bta.addObject(obj=xml_data, name=path, path=None)

4057 4058 4059 4060
class MessageTranslationTemplateItem(BaseTemplateItem):

  def build(self, context, **kw):
    localizer = context.getPortalObject().Localizer
4061 4062 4063 4064 4065 4066
    for lang_key in self._archive.keys():
      if '|' in lang_key:
        lang, catalog = lang_key.split(' | ')
      else: # XXX backward compatibilty
        lang = lang_key
        catalog = 'erp5_ui'
4067
      path = posixpath.join(lang, catalog)
4068 4069
      mc = localizer._getOb(catalog)
      self._objects[path] = mc.manage_export(lang)
4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100

  def preinstall(self, context, installed_bt, **kw):
    modified_object_list = {}
    if context.getTemplateFormatVersion() == 1:
      new_keys = self._objects.keys()
      for path in new_keys:
        if installed_bt._objects.has_key(path):
          # compare object to see if there is changes
          new_obj_code = self._objects[path]
          old_obj_code = installed_bt._objects[path]
          if new_obj_code != old_obj_code:
            modified_object_list.update({path : ['Modified', self.__class__.__name__[:-12]]})
        else: # new object
          modified_object_list.update({path : ['New', self.__class__.__name__[:-12]]})
      # get removed object
      old_keys = installed_bt._objects.keys()
      for path in old_keys:
        if path not in new_keys:
          modified_object_list.update({path : ['Removed', self.__class__.__name__[:-12]]})
    return modified_object_list

  def install(self, context, trashbin, **kw):
    localizer = context.getPortalObject().Localizer
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    if context.getTemplateFormatVersion() == 1:
      for path, po in self._objects.items():
        if update_dict.has_key(path) or force:
          if not force:
            action = update_dict[path]
            if action == 'nothing':
4101
              continue
4102
          path = path.split('/')
4103 4104 4105 4106 4107 4108
          if len(path) == 2:
            lang = path[0]
            catalog = path[1]
          else:
            lang = path[-3]
            catalog = path[-2]
4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134
          if lang not in localizer.get_languages():
            localizer.manage_addLanguage(lang)
          mc = localizer._getOb(catalog)
          if lang not in mc.get_languages():
            mc.manage_addLanguage(lang)
          mc.manage_import(lang, po)
    else:
      BaseTemplateItem.install(self, context, trashbin, **kw)
      for lang, catalogs in self._archive.items():
        if lang not in localizer.get_languages():
          localizer.manage_addLanguage(lang)
        for catalog, po in catalogs.items():
          mc = localizer._getOb(catalog)
          if lang not in mc.get_languages():
            mc.manage_addLanguage(lang)
          mc.manage_import(lang, po)

  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=root_path)
    for key in self._objects.keys():
      obj = self._objects[key]
      path = os.path.join(root_path, key)
      bta.addFolder(name=path)
4135
      f = open(path+os.sep+'translation.po', 'wb')
4136 4137 4138 4139
      f.write(str(obj))
      f.close()

  def _importFile(self, file_name, file):
4140
    if posixpath.split(file_name)[1] == 'translation.po':
4141 4142
      text = file.read()
      self._objects[file_name[:-3]] = text
4143

Aurel's avatar
Aurel committed
4144 4145
class LocalRolesTemplateItem(BaseTemplateItem):

4146
  def __init__(self, id_list, **kw):
4147
    id_list = ['local_roles/%s' % id for id in id_list if id != '']
4148 4149
    BaseTemplateItem.__init__(self, id_list, **kw)

Aurel's avatar
Aurel committed
4150 4151 4152
  def build(self, context, **kw):
    p = context.getPortalObject()
    for path in self._archive.keys():
4153
      obj = p.unrestrictedTraverse(path.split('/', 1)[1])
4154 4155 4156 4157
      local_roles_dict = getattr(obj, '__ac_local_roles__',
                                        {}) or {}
      group_local_roles_dict = getattr(obj, '__ac_local_group_roles__',
                                        {}) or {}
Aurel's avatar
Aurel committed
4158 4159
      self._objects[path] = (local_roles_dict, group_local_roles_dict)

Christophe Dumez's avatar
Christophe Dumez committed
4160
  # Function to generate XML Code Manually
Aurel's avatar
Aurel committed
4161 4162 4163 4164 4165 4166 4167 4168
  def generateXml(self, path=None):
    local_roles_dict, group_local_roles_dict = self._objects[path]
    local_roles_keys = local_roles_dict.keys()
    group_local_roles_keys = group_local_roles_dict.keys()
    local_roles_keys.sort()
    group_local_roles_keys.sort()
    # local roles
    xml_data = '<local_roles_item>'
4169
    xml_data += '\n <local_roles>'
Aurel's avatar
Aurel committed
4170
    for key in local_roles_keys:
4171
      xml_data += "\n  <role id='%s'>" %(key,)
Aurel's avatar
Aurel committed
4172 4173
      tuple = local_roles_dict[key]
      for item in tuple:
4174 4175 4176
        xml_data += "\n   <item>%s</item>" %(item,)
      xml_data += '\n  </role>'
    xml_data += '\n </local_roles>'
Aurel's avatar
Aurel committed
4177
    # group local roles
4178
    xml_data += '\n <group_local_roles>'
Aurel's avatar
Aurel committed
4179
    for key in group_local_roles_keys:
4180
      xml_data += "\n  <role id='%s'>" %(key,)
Aurel's avatar
Aurel committed
4181 4182
      tuple = group_local_roles_dict[key]
      for item in tuple:
4183 4184 4185 4186
        xml_data += '\n   <item>%s</item>' %(item,)
      xml_data += '\n  </role>'
    xml_data += '\n </group_local_roles>'
    xml_data += '\n</local_roles_item>'
Aurel's avatar
Aurel committed
4187
    return xml_data
4188

Aurel's avatar
Aurel committed
4189 4190 4191 4192 4193 4194 4195 4196
  def export(self, context, bta, **kw):
    if len(self._objects.keys()) == 0:
      return
    root_path = os.path.join(bta.path, self.__class__.__name__)
    bta.addFolder(name=root_path)
    for key in self._objects.keys():
      xml_data = self.generateXml(key)

4197
      folders, id = posixpath.split(key)
Aurel's avatar
Aurel committed
4198
      encode_folders = []
4199
      for folder in folders.split('/')[1:]:
Aurel's avatar
Aurel committed
4200
        if '%' not in folder:
4201
          encode_folders.append(quote(folder))
Aurel's avatar
Aurel committed
4202 4203
        else:
          encode_folders.append(folder)
4204
      path = os.path.join(root_path, (os.sep).join(encode_folders))
Aurel's avatar
Aurel committed
4205 4206 4207 4208 4209 4210 4211
      bta.addFolder(name=path)
      bta.addObject(obj=xml_data, name=id, path=path)

  def _importFile(self, file_name, file):
    xml = parse(file)
    # local roles
    local_roles = xml.getElementsByTagName('local_roles')[0]
4212
    local_roles_list = local_roles.getElementsByTagName('role')
Aurel's avatar
Aurel committed
4213 4214 4215
    local_roles_dict = {}
    for role in local_roles_list:
      id = role.getAttribute('id')
4216 4217
      if isinstance(id, unicode):
        id = id.encode('utf-8')
Aurel's avatar
Aurel committed
4218 4219 4220 4221 4222 4223 4224
      item_type_list = []
      item_list = role.getElementsByTagName('item')
      for item in item_list:
        item_type_list.append(str(item.childNodes[0].data))
      local_roles_dict[id] = item_type_list
    # group local roles
    group_local_roles = xml.getElementsByTagName('group_local_roles')[0]
4225
    local_roles_list = group_local_roles.getElementsByTagName('role')
Aurel's avatar
Aurel committed
4226 4227 4228
    group_local_roles_dict = {}
    for role in local_roles_list:
      id = role.getAttribute('id')
4229 4230
      if isinstance(id, unicode):
        id = id.encode('utf-8')
Aurel's avatar
Aurel committed
4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260
      item_type_list = []
      item_list = role.getElementsByTagName('item')
      for item in item_list:
        item_type_list.append(str(item.childNodes[0].data))
      group_local_roles_dict[id] = item_type_list
    self._objects['local_roles/'+file_name[:-4]] = (local_roles_dict, group_local_roles_dict)

  def install(self, context, trashbin, **kw):
    update_dict = kw.get('object_to_update')
    force = kw.get('force')
    p = context.getPortalObject()
    for roles_path in self._objects.keys():
      if update_dict.has_key(roles_path) or force:
        if not force:
          action = update_dict[roles_path]
          if action == 'nothing':
            continue
      path = roles_path.split('/')[1:]
      obj = p.unrestrictedTraverse(path)
      local_roles_dict, group_local_roles_dict = self._objects[roles_path]
      setattr(obj, '__ac_local_roles__', local_roles_dict)
      setattr(obj, '__ac_local_group_roles__', group_local_roles_dict)

  def uninstall(self, context, **kw):
    p = context.getPortalObject()
    for roles_path in self._objects.keys():
      path = roles_path.split('/')[1:]
      obj = p.unrestrictedTraverse(path)
      setattr(obj, '__ac_local_roles__', {})
      setattr(obj, '__ac_local_group_roles__', {})
4261

4262 4263 4264
class BusinessTemplate(XMLObject):
    """
    A business template allows to construct ERP5 modules
Christophe Dumez's avatar
Christophe Dumez committed
4265
    in part or completely. Each object is separated from its
4266 4267 4268 4269 4270 4271 4272 4273 4274 4275
    subobjects and exported in xml format.
    It may include:

    - catalog definition
      - SQL method objects
      - SQL methods including:
        - purpose (catalog, uncatalog, etc.)
        - filter definition

    - portal_types definition
Christophe Dumez's avatar
Christophe Dumez committed
4276 4277
      - object without optimal actions
      - list of relation between portal type and workflow
4278 4279 4280 4281 4282 4283 4284 4285 4286 4287

    - module definition
      - id
      - title
      - portal type
      - roles/security

    - site property definition
      - id
      - type
4288
      - value
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4289

4290 4291
    - document/propertysheet/extension/test definition
      - copy of the local file
4292

4293
    - message transalation definition
4294
      - .po file
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4295

4296 4297
    The Business Template properties are exported to the bt folder with
    one property per file
4298

Jean-Paul Smets's avatar
Jean-Paul Smets committed
4299 4300
    Technology:

4301 4302
    - download a gzip file or folder tree (from the web, from a CVS repository,
      from local file system) (import/donwload)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4303

4304
    - install files to the right location (install)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4305 4306 4307 4308

    Use case:

    - install core ERP5 (the minimum)
4309

4310
    - go to "BT" menu. Import BT. Select imported BT. Click install.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4311

4312 4313
    - go to "BT" menu. Create new BT.
      Define BT elements (workflow, methods, attributes, etc.).
4314
      Build BT and export or save it
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4315 4316 4317 4318 4319
      Done.
    """

    meta_type = 'ERP5 Business Template'
    portal_type = 'Business Template'
4320
    add_permission = Permissions.AddPortalContent
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4321 4322 4323 4324 4325
    isPortalContent = 1
    isRADContent = 1

    # Declarative security
    security = ClassSecurityInfo()
4326
    security.declareObjectProtected(Permissions.AccessContentsInformation)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4327 4328 4329 4330

    # Declarative properties
    property_sheets = ( PropertySheet.Base
                      , PropertySheet.XMLObject
4331
                      , PropertySheet.SimpleItem
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4332
                      , PropertySheet.CategoryCore
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4333
                      , PropertySheet.Version
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4334
                      , PropertySheet.BusinessTemplate
4335
                      , PropertySheet.Comment
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4336 4337
                      )

4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354
    # Factory Type Information
    factory_type_information = \
      {    'id'             : portal_type
         , 'meta_type'      : meta_type
         , 'description'    : """\
Business Template is a set of definitions, such as skins, portal types and categories. This is used to set up a new ERP5 site very efficiently."""
         , 'icon'           : 'order_line_icon.gif'
         , 'product'        : 'ERP5Type'
         , 'factory'        : 'addBusinessTemplate'
         , 'immediate_view' : 'BusinessTemplate_view'
         , 'allow_discussion'     : 1
         , 'allowed_content_types': (
                                      )
         , 'filter_content_types' : 1
         , 'global_allow'   : 1
      }

4355 4356
    # This is a global variable
    # Order is important for installation
4357 4358 4359 4360 4361 4362
    # We want to have:
    #  * path after module, because path can be module content
    #  * path after categories, because path can be categories content
    #  * skin after paths, because we can install a custom connection string as
    #       path and use it with SQLMethods in a skin.
    #    ( and more )
4363 4364 4365
    _item_name_list = [
      '_product_item',
      '_property_sheet_item',
4366
      '_constraint_item',
4367 4368 4369 4370
      '_document_item',
      '_extension_item',
      '_test_item',
      '_role_item',
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4371
      '_tool_item',
4372 4373 4374 4375
      '_message_translation_item',
      '_workflow_item',
      '_site_property_item',
      '_portal_type_item',
4376
      '_portal_type_workflow_chain_item',
4377 4378 4379 4380
      '_portal_type_allowed_content_type_item',
      '_portal_type_hidden_content_type_item',
      '_portal_type_property_sheet_item',
      '_portal_type_base_category_item',
4381 4382
      '_category_item',
      '_module_item',
4383
      '_path_item',
4384
      '_skin_item',
4385
      '_preference_item',
4386
      '_action_item',
4387
      '_portal_type_roles_item',
4388
      '_local_roles_item',
4389
      '_catalog_method_item',
4390 4391 4392
      '_catalog_result_key_item',
      '_catalog_related_key_item',
      '_catalog_result_table_item',
4393
      '_catalog_keyword_key_item',
4394
      '_catalog_datetime_key_item',
4395 4396 4397 4398
      '_catalog_full_text_key_item',
      '_catalog_request_key_item',
      '_catalog_multivalue_key_item',
      '_catalog_topic_key_item',
4399
      '_catalog_scriptable_key_item',
4400 4401
      '_catalog_role_key_item',
      '_catalog_local_role_key_item',
4402 4403 4404 4405
    ]

    def __init__(self, *args, **kw):
      XMLObject.__init__(self, *args, **kw)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4406 4407 4408 4409 4410 4411
      self._clean()

    def getTemplateFormatVersion(self, **kw):
      """This is a workaround, because template_format_version was not set even for the new format.
      """
      if self.hasProperty('template_format_version'):
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4412
        self._baseGetTemplateFormatVersion()
4413

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4414 4415 4416
      # the attribute _objects in BaseTemplateItem was added in the new format.
      if hasattr(self._path_item, '_objects'):
        return 1
4417

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4418
      return 0
4419

4420
    security.declareProtected(Permissions.ManagePortal, 'manage_afterAdd')
4421 4422 4423 4424 4425 4426 4427
    def manage_afterAdd(self, item, container):
      """
        This is called when a new business template is added or imported.
      """
      portal_workflow = getToolByName(self, 'portal_workflow')
      if portal_workflow is not None:
        # Make sure that the installation state is "not installed".
4428 4429
        if portal_workflow.getStatusOf(
                'business_template_installation_workflow', self) is not None:
4430
          # XXX Not good to access the attribute directly,
4431 4432 4433
          # but there is no API for clearing the history.
          self.workflow_history[
                            'business_template_installation_workflow'] = None
4434

4435 4436
    security.declareProtected(Permissions.AccessContentsInformation,
                              'getRevision')
4437 4438 4439 4440 4441 4442
    def getRevision(self):
      """returns the revision property.
      This is a workaround for #461.
      """
      return self._baseGetRevision()

4443
    def updateRevisionNumber(self):
Jérome Perrin's avatar
Jérome Perrin committed
4444 4445 4446 4447 4448 4449 4450 4451
        """Increment bt revision number.
        """
        revision_number = self.getRevision()
        if revision_number is None or revision_number.strip() == '':
          revision_number = 1
        else:
          revision_number = int(revision_number)+1
        self.setRevision(revision_number)
4452

4453
    security.declareProtected(Permissions.ManagePortal, 'build')
4454
    def build(self, no_action=0):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4455 4456 4457
      """
        Copy existing portal objects to self
      """
4458
      if no_action: return
4459
        # this is use at import of Business Template to get the status built
4460 4461
      # Make sure that everything is sane.
      self.clean()
4462

4463
      self.updateRevisionNumber()
4464

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4465 4466
      self._setTemplateFormatVersion(1)

4467
      # Store all data
4468 4469
      self._portal_type_item = \
          PortalTypeTemplateItem(self.getTemplatePortalTypeIdList())
4470
      self._portal_type_workflow_chain_item = \
4471
          PortalTypeWorkflowChainTemplateItem(self.getTemplatePortalTypeWorkflowChainList())
4472 4473 4474 4475 4476 4477 4478 4479 4480 4481
      self._workflow_item = \
          WorkflowTemplateItem(self.getTemplateWorkflowIdList())
      self._skin_item = \
          SkinTemplateItem(self.getTemplateSkinIdList())
      self._category_item = \
          CategoryTemplateItem(self.getTemplateBaseCategoryList())
      self._catalog_method_item = \
          CatalogMethodTemplateItem(self.getTemplateCatalogMethodIdList())
      self._action_item = \
          ActionTemplateItem(self.getTemplateActionPathList())
4482 4483
      self._portal_type_roles_item = \
          PortalTypeRolesTemplateItem(self.getTemplatePortalTypeRolesList())
4484 4485 4486 4487 4488 4489 4490 4491
      self._site_property_item = \
          SitePropertyTemplateItem(self.getTemplateSitePropertyIdList())
      self._module_item = \
          ModuleTemplateItem(self.getTemplateModuleIdList())
      self._document_item = \
          DocumentTemplateItem(self.getTemplateDocumentIdList())
      self._property_sheet_item = \
          PropertySheetTemplateItem(self.getTemplatePropertySheetIdList())
4492 4493
      self._constraint_item = \
          ConstraintTemplateItem(self.getTemplateConstraintIdList())
4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513
      self._extension_item = \
          ExtensionTemplateItem(self.getTemplateExtensionIdList())
      self._test_item = \
          TestTemplateItem(self.getTemplateTestIdList())
      self._product_item = \
          ProductTemplateItem(self.getTemplateProductIdList())
      self._role_item = \
          RoleTemplateItem(self.getTemplateRoleList())
      self._catalog_result_key_item = \
          CatalogResultKeyTemplateItem(
               self.getTemplateCatalogResultKeyList())
      self._catalog_related_key_item = \
          CatalogRelatedKeyTemplateItem(
               self.getTemplateCatalogRelatedKeyList())
      self._catalog_result_table_item = \
          CatalogResultTableTemplateItem(
               self.getTemplateCatalogResultTableList())
      self._message_translation_item = \
          MessageTranslationTemplateItem(
               self.getTemplateMessageTranslationList())
4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525
      self._portal_type_allowed_content_type_item = \
           PortalTypeAllowedContentTypeTemplateItem(
               self.getTemplatePortalTypeAllowedContentTypeList())
      self._portal_type_hidden_content_type_item = \
           PortalTypeHiddenContentTypeTemplateItem(
               self.getTemplatePortalTypeHiddenContentTypeList())
      self._portal_type_property_sheet_item = \
           PortalTypePropertySheetTemplateItem(
               self.getTemplatePortalTypePropertySheetList())
      self._portal_type_base_category_item = \
           PortalTypeBaseCategoryTemplateItem(
               self.getTemplatePortalTypeBaseCategoryList())
4526 4527
      self._path_item = \
               PathTemplateItem(self.getTemplatePathList())
4528 4529
      self._preference_item = \
               PreferenceTemplateItem(self.getTemplatePreferenceList())
4530 4531
      self._catalog_keyword_key_item = \
          CatalogKeywordKeyTemplateItem(
4532
               self.getTemplateCatalogKeywordKeyList())
4533 4534 4535
      self._catalog_datetime_key_item = \
          CatalogDateTimeKeyTemplateItem(
               self.getTemplateCatalogDatetimeKeyList())
4536 4537
      self._catalog_full_text_key_item = \
          CatalogFullTextKeyTemplateItem(
4538
               self.getTemplateCatalogFullTextKeyList())
4539 4540
      self._catalog_request_key_item = \
          CatalogRequestKeyTemplateItem(
4541
               self.getTemplateCatalogRequestKeyList())
4542 4543
      self._catalog_multivalue_key_item = \
          CatalogMultivalueKeyTemplateItem(
4544
               self.getTemplateCatalogMultivalueKeyList())
4545 4546 4547
      self._catalog_topic_key_item = \
          CatalogTopicKeyTemplateItem(
               self.getTemplateCatalogTopicKeyList())
Aurel's avatar
Aurel committed
4548 4549 4550
      self._local_roles_item = \
          LocalRolesTemplateItem(
               self.getTemplateLocalRolesList())
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4551 4552 4553
      self._tool_item = \
          ToolTemplateItem(
               self.getTemplateToolIdList())
4554 4555 4556
      self._catalog_scriptable_key_item = \
          CatalogScriptableKeyTemplateItem(
               self.getTemplateCatalogScriptableKeyList())
4557 4558 4559 4560 4561 4562
      self._catalog_role_key_item = \
          CatalogRoleKeyTemplateItem(
               self.getTemplateCatalogRoleKeyList())
      self._catalog_local_role_key_item = \
          CatalogLocalRoleKeyTemplateItem(
               self.getTemplateCatalogLocalRoleKeyList())
4563

4564 4565 4566
      # Build each part
      for item_name in self._item_name_list:
        getattr(self, item_name).build(self)
4567

4568
    build = WorkflowMethod(build)
4569 4570

    def publish(self, url, username=None, password=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4571
      """
4572
        Publish in a format or another
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4573
      """
4574
      return self.portal_templates.publish(self, url, username=username,
4575
                                           password=password)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4576

4577
    def update(self):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4578
      """
4579
        Update template: download new template definition
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4580
      """
4581
      return self.portal_templates.update(self)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4582

4583 4584 4585 4586 4587
    def isCatalogUpdatable(self):
      """
      Return if catalog will be updated or not by business template installation
      """
      catalog_method = getattr(self, '_catalog_method_item', None)
4588 4589 4590 4591 4592 4593 4594 4595 4596 4597
      default_catalog = self.getPortalObject().portal_catalog.getSQLCatalog()
      my_catalog = _getCatalogValue(self)
      if default_catalog is not None and my_catalog is not None \
             and catalog_method is not None and self.getTemplateFormatVersion() == 1:
        if default_catalog.getId() == my_catalog.getId():
          # It is needed to update the catalog only if the default SQLCatalog is modified.
          for method_id in catalog_method._objects.keys():
            if 'related' not in method_id:
              # must update catalog
              return True
4598 4599
      return False

4600
    def preinstall(self, check_dependencies=1, **kw):
4601 4602 4603
      """
        Return the list of modified/new/removed object between a Business Template
        and the one installed if exists
Aurel's avatar
Aurel committed
4604
      """
4605

4606 4607 4608 4609 4610
      if check_dependencies:
        # required because in multi installation, dependencies has already
        # been checked before and it will failed here as dependencies can be
        # installed at the same time
        self.checkDependencies()
4611

4612 4613
      modified_object_list = {}
      bt_title = self.getTitle()
4614 4615 4616 4617 4618 4619 4620

      #  can be call to diff two Business Template in template tool
      bt2 = kw.get('compare_to', None)
      if  bt2 is not None:
        installed_bt = bt2
      else:
        installed_bt = self.portal_templates.getInstalledBusinessTemplate(title=bt_title)
4621 4622 4623
      if installed_bt is None:
        installed_bt_format = 0 # that will not check for modification
      else:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4624
        installed_bt_format = installed_bt.getTemplateFormatVersion()
4625

4626
      # if reinstall business template, must compare to object in ZODB
4627
      # and not to those in the installed Business Template because it is itself.
Christophe Dumez's avatar
Christophe Dumez committed
4628
      # same if we make a diff and select only one business template
4629
      reinstall = 0
4630 4631
      if installed_bt == self:
        reinstall = 1
4632
        bt2 = self.portal_templates.manage_clone(ob=installed_bt, id=INSTALLED_BT_FOR_DIFF)
4633 4634
        # update portal types properties to get last modifications
        bt2.getPortalTypesProperties()
4635 4636 4637
        bt2.edit(description='tmp bt generated for diff')
        bt2.build()
        installed_bt = bt2
4638

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4639
      new_bt_format = self.getTemplateFormatVersion()
4640 4641 4642 4643 4644 4645
      if installed_bt_format == 0 and new_bt_format == 0:
        # still use old format, so install everything, no choice
        return modified_object_list
      elif installed_bt_format == 0 and new_bt_format == 1:
        # return list of all object in bt
        for item_name in self._item_name_list:
4646
          item = getattr(self, item_name, None)
4647 4648 4649 4650
          if item is not None:
            for path in item._objects.keys():
              modified_object_list.update({path : ['New', item.__class__.__name__[:-12]]})
        return modified_object_list
4651

4652
      for item_name in self._item_name_list:
4653
        new_item = getattr(self, item_name, None)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4654
        old_item = getattr(installed_bt, item_name, None)
4655
        if new_item is not None:
4656
          if old_item is not None and hasattr(old_item, '_objects'):
4657
            modified_object = new_item.preinstall(context=self, installed_bt=old_item)
4658 4659 4660 4661 4662 4663 4664
            if len(modified_object) > 0:
              modified_object_list.update(modified_object)
          else:
            for path in new_item._objects.keys():
              modified_object_list.update({path : ['New', new_item.__class__.__name__[:-12]]})

      if reinstall:
4665
        self.portal_templates.manage_delObjects(ids=[INSTALLED_BT_FOR_DIFF])
4666

4667 4668
      return modified_object_list

4669 4670
    def _install(self, force=1, object_to_update=None, update_translation=0,
                 update_catalog=0, **kw):
4671
      """
Christophe Dumez's avatar
Christophe Dumez committed
4672
        Install a new Business Template, if force, all will be upgraded or installed
4673 4674
        otherwise depends of dict object_to_update
      """
4675 4676 4677 4678
      if object_to_update is not None:
        force=0
      else:
        object_to_update = {}
4679

4680 4681
      installed_bt = self.portal_templates.getInstalledBusinessTemplate(
                                                           self.getTitle())
4682 4683
      # When reinstalling, installation state should not change to replaced
      if installed_bt not in [None, self]:
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4684
        if installed_bt.getTemplateFormatVersion() == 0:
4685
          force = 1
4686
        installed_bt.replace(self)
4687

4688 4689 4690 4691
      trash_tool = getToolByName(self, 'portal_trash', None)
      if trash_tool is None and self.getTemplateFormatVersion() == 1:
        raise AttributeError, 'Trash Tool is not installed'

4692 4693 4694
      # Check the format of business template, if old, force install
      if self.getTemplateFormatVersion() == 0:
        force = 1
4695

4696 4697 4698
      if not force:
        self.checkDependencies()

4699
      site = self.getPortalObject()
4700 4701 4702 4703 4704 4705
      custom_generator_class = getattr(site, '_generator_class', None)
      if custom_generator_class is not None:
        gen = custom_generator_class()
      else:
        from Products.ERP5.ERP5Site import ERP5Generator
        gen = ERP5Generator()
4706 4707 4708
      # update activity tool first if necessary
      if self.getTitle() == 'erp5_core' and self.getTemplateUpdateTool():
        LOG('Business Template', 0, 'Updating Activity Tool')
4709
        gen.setupLastTools(site, update=1, create_activities=1)
4710 4711
      if not force:
        if len(object_to_update) == 0:
4712 4713 4714 4715 4716
          # check if we have to update tools
          if self.getTitle() == 'erp5_core' and self.getTemplateUpdateTool():
            LOG('Business Template', 0, 'Updating Tools')
            gen.setup(site, 0, update=1)
          if self.getTitle() == 'erp5_core' and self.getTemplateUpdateBusinessTemplateWorkflow():
4717
            LOG('Business Template', 0, 'Updating Business Template Workflows')
4718
            gen.setupWorkflow(site)
4719
          return
4720

4721 4722
      # always created a trash bin because we may to save object already present
      # but not in a previous business templates apart at creation of a new site
4723
      if trash_tool is not None and (len(object_to_update) > 0 or len(self.portal_templates) > 1):
4724 4725 4726 4727 4728
        trashbin = trash_tool.newTrashBin(self.getTitle(), self)
      else:
        trashbin = None

      # Install everything
4729
      if len(object_to_update) or force:
4730 4731 4732
        for item_name in self._item_name_list:
          item = getattr(self, item_name, None)
          if item is not None:
4733
            item.install(self, force=force, object_to_update=object_to_update, trashbin=trashbin)
4734

4735
      # update catalog if necessary
4736 4737
      if force and self.isCatalogUpdatable():
        update_catalog = 1
4738
      if update_catalog:
4739
        catalog = _getCatalogValue(self)
4740
        if (catalog is None) or (not site.isIndexable):
4741 4742 4743 4744 4745
          LOG('Business Template', 0, 'no SQL Catalog available')
          update_catalog = 0
        else:
          LOG('Business Template', 0, 'Updating SQL Catalog')
          catalog.manage_catalogClear()
4746

4747
      # get objects to remove
4748
      # do remove after because we may need backup object from installation
4749
      remove_object_dict = {}
4750 4751
      for path, action in object_to_update.iteritems():
        if action in ('remove', 'save_and_remove'):
4752
          remove_object_dict[path] = action
4753

4754
      # remove object from old business template
4755
      if len(remove_object_dict):
4756
        # XXX: this code assumes that there is an installed_bt
4757
        for item_name in installed_bt._item_name_list:
4758
          item = getattr(installed_bt, item_name, None)
4759
          if item is not None:
4760
            item.remove(self, remove_object_dict=remove_object_dict, trashbin=trashbin)
4761

4762 4763 4764 4765 4766 4767

      # update tools if necessary
      if self.getTitle() == 'erp5_core' and self.getTemplateUpdateTool():
        LOG('Business Template', 0, 'Updating Tools')
        gen.setup(site, 0, update=1)

4768
      # check if we have to update business template workflow
4769
      if self.getTitle() == 'erp5_core' and self.getTemplateUpdateBusinessTemplateWorkflow():
4770
        LOG('Business Template', 0, 'Updating Business Template Workflows')
4771 4772 4773 4774 4775
        gen.setupWorkflow(site)
        # XXX keep TM in case update of workflow doesn't work
        #         self._v_txn = WorkflowUpdateTM()
        #         self._v_txn.register(update=1, gen=gen, site=site)

4776 4777
      # remove trashbin if empty
      if trashbin is not None:
4778
        if len(trashbin) == 0:
4779 4780
          trash_tool.manage_delObjects([trashbin.getId(),])

4781 4782
      if update_catalog:
        site.ERP5Site_reindexAll()
4783

4784 4785
      # Update translation table, in case we added new portal types or
      # workflow states.
4786 4787
      if update_translation:
        site.ERP5Site_updateTranslationTable()
4788

4789 4790
      # Clear cache to avoid reusing cached values with replaced objects.
      site.portal_caches.clearAllCache()
4791

4792
    security.declareProtected(Permissions.ManagePortal, 'install')
4793 4794 4795
    def install(self, **kw):
      """
        For install based on paramaters provided in **kw
4796
      """
4797
      return self._install(**kw)
4798

4799
    install = WorkflowMethod(install)
4800

4801
    security.declareProtected(Permissions.ManagePortal, 'reinstall')
4802
    def reinstall(self, **kw):
4803 4804 4805 4806
      """Reinstall Business Template.
      """
      return self._install(**kw)

4807
    reinstall = WorkflowMethod(reinstall)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4808

4809
    security.declareProtected(Permissions.ManagePortal, 'trash')
4810 4811
    def trash(self, new_bt, **kw):
      """
4812
        Trash unnecessary items before upgrading to a new business
4813
        template.
4814
        This is similar to uninstall, but different in that this does
4815
        not remove all items.
4816
      """
4817 4818
      # Trash everything
      for item_name in self._item_name_list[::-1]:
4819
        item = getattr(self, item_name, None)
4820 4821
        if item is not None:
          item.trash(
4822
                self,
4823
                getattr(new_bt, item_name))
4824

4825
    security.declareProtected(Permissions.ManagePortal, 'uninstall')
4826
    def uninstall(self, **kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4827
      """
4828
        For uninstall based on paramaters provided in **kw
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4829
      """
4830 4831 4832
      # Uninstall everything
      # Trash everything
      for item_name in self._item_name_list[::-1]:
4833
        item = getattr(self, item_name, None)
4834
        if item is not None:
4835
          item.uninstall(self)
4836
      # It is better to clear cache because the uninstallation of a
4837
      # template deletes many things from the portal.
Aurel's avatar
Aurel committed
4838
      self.getPortalObject().portal_caches.clearAllCache()
4839

4840 4841
    uninstall = WorkflowMethod(uninstall)

4842
    security.declareProtected(Permissions.ManagePortal, 'clean')
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4843
    def _clean(self):
4844
      """
4845
        Clean built information.
4846
      """
4847
      # First, remove obsolete attributes if present.
4848 4849 4850 4851
      for attr in ( '_action_archive',
                    '_document_archive',
                    '_extension_archive',
                    '_test_archive',
4852
                    '_module_archive',
4853 4854 4855
                    '_object_archive',
                    '_portal_type_archive',
                    '_property_archive',
4856
                    '_property_sheet_archive'):
4857 4858 4859
        if hasattr(self, attr):
          delattr(self, attr)
      # Secondly, make attributes empty.
4860 4861
      for item_name in self._item_name_list:
        item = setattr(self, item_name, None)
4862

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4863
    clean = WorkflowMethod(_clean)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4864

4865
    security.declareProtected(Permissions.AccessContentsInformation,
4866
                              'getBuildingState')
4867
    def getBuildingState(self, default=None, id_only=1):
4868
      """
4869
        Returns the current state in building
4870
      """
4871
      portal_workflow = getToolByName(self, 'portal_workflow')
4872 4873
      wf = portal_workflow.getWorkflowById(
                          'business_template_building_workflow')
4874
      return wf._getWorkflowStateOf(self, id_only=id_only )
4875

4876
    security.declareProtected(Permissions.AccessContentsInformation,
4877
                              'getInstallationState')
4878
    def getInstallationState(self, default=None, id_only=1):
4879
      """
4880
        Returns the current state in installation
4881
      """
4882
      portal_workflow = getToolByName(self, 'portal_workflow')
4883 4884
      wf = portal_workflow.getWorkflowById(
                           'business_template_installation_workflow')
4885
      return wf._getWorkflowStateOf(self, id_only=id_only )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4886

Yoshinori Okuji's avatar
Yoshinori Okuji committed
4887 4888 4889 4890 4891 4892
    security.declareProtected(Permissions.AccessContentsInformation, 'toxml')
    def toxml(self):
      """
        Return this Business Template in XML
      """
      portal_templates = getToolByName(self, 'portal_templates')
4893
      export_string = portal_templates.manage_exportObject(
4894 4895
                                               id=self.getId(),
                                               toxml=1,
4896
                                               download=1)
Yoshinori Okuji's avatar
Yoshinori Okuji committed
4897
      return export_string
4898

4899
    def _getOrderedList(self, id):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4900
      """
4901 4902
        We have to set this method because we want an
        ordered list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4903
      """
4904
      result = getattr(self, id, ())
4905 4906 4907 4908
      if result is None: result = ()
      if result != ():
        result = list(result)
        result.sort()
4909
        # XXX Why do we need to return a tuple ?
4910 4911
        result = tuple(result)
      return result
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4912

4913
    def getTemplateCatalogMethodIdList(self):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4914
      """
4915 4916
      We have to set this method because we want an
      ordered list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4917
      """
4918
      return self._getOrderedList('template_catalog_method_id')
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4919

4920
    def getTemplateBaseCategoryList(self):
4921
      """
4922 4923
      We have to set this method because we want an
      ordered list
4924
      """
4925
      return self._getOrderedList('template_base_category')
4926

4927
    def getTemplateWorkflowIdList(self):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4928
      """
4929 4930
      We have to set this method because we want an
      ordered list
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4931
      """
4932
      return self._getOrderedList('template_workflow_id')
4933

4934
    def getTemplatePortalTypeIdList(self):
4935
      """
4936 4937
      We have to set this method because we want an
      ordered list
4938
      """
4939
      return self._getOrderedList('template_portal_type_id')
4940

4941 4942 4943 4944 4945 4946 4947
    def getTemplatePortalTypeWorkflowChainList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_workflow_chain')

Alexandre Boeglin's avatar
Alexandre Boeglin committed
4948 4949 4950 4951 4952 4953 4954
    def getTemplatePathList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_path')

4955 4956 4957 4958 4959 4960 4961
    def getTemplatePreferenceList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_preference')

4962 4963 4964 4965 4966 4967
    def getTemplatePortalTypeAllowedContentTypeList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_allowed_content_type')
4968

4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989
    def getTemplatePortalTypeHiddenContentTypeList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_hidden_content_type')

    def getTemplatePortalTypePropertySheetList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_property_sheet')

    def getTemplatePortalTypeBaseCategoryList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_base_category')

4990
    def getTemplateActionPathList(self):
4991
      """
4992 4993
      We have to set this method because we want an
      ordered list
4994
      """
4995
      return self._getOrderedList('template_action_path')
4996

4997 4998 4999 5000 5001 5002 5003
    def getTemplatePortalTypeRolesList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_portal_type_roles')

5004
    def getTemplateSkinIdList(self):
5005
      """
5006 5007
      We have to set this method because we want an
      ordered list
5008
      """
5009
      return self._getOrderedList('template_skin_id')
5010

5011
    def getTemplateModuleIdList(self):
5012
      """
5013 5014
      We have to set this method because we want an
      ordered list
5015
      """
5016
      return self._getOrderedList('template_module_id')
5017 5018 5019 5020 5021 5022 5023

    def getTemplateMessageTranslationList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_message_translation')
5024

Yoshinori Okuji's avatar
Yoshinori Okuji committed
5025 5026 5027 5028 5029 5030 5031
    def getTemplateToolIdList(self):
      """
      We have to set this method because we want an
      ordered list
      """
      return self._getOrderedList('template_tool_id')

5032
    security.declareProtected(Permissions.ManagePortal, 'export')
Aurel's avatar
Aurel committed
5033 5034 5035 5036
    def export(self, path=None, local=0, **kw):
      """
        Export this Business Template
      """
5037
      if self.getBuildingState() != 'built':
5038
        raise TemplateConditionError, \
Christophe Dumez's avatar
Christophe Dumez committed
5039
              'Business Template must be built before export'
5040 5041 5042
      if self.getInstallationState() == 'installed':
        raise TemplateConditionError, \
              'Can not export installed Business Template'
5043

Aurel's avatar
Aurel committed
5044 5045 5046 5047 5048 5049 5050
      if local:
        # we export into a folder tree
        bta = BusinessTemplateFolder(creation=1, path=path)
      else:
        # We export BT into a tarball file
        bta = BusinessTemplateTarball(creation=1, path=path)

5051
      # export bt
5052
      bta.addFolder(path+os.sep+'bt')
Aurel's avatar
Aurel committed
5053
      for prop in self.propertyMap():
5054
        prop_type = prop['type']
Aurel's avatar
Aurel committed
5055
        id = prop['id']
5056
        if id in ('id', 'uid', 'rid', 'sid', 'id_group', 'last_id',
5057
                  'install_object_list_list', 'id_generator'):
5058
          continue
Aurel's avatar
Aurel committed
5059
        value = self.getProperty(id)
5060
        if prop_type in ('text', 'string', 'int', 'boolean'):
5061
          bta.addObject(obj=value, name=id, path=path+os.sep+'bt', ext='')
5062
        elif prop_type in ('lines', 'tokens'):
5063
          bta.addObject(obj=str('\n').join(value), name=id,
5064
                        path=path+os.sep+'bt', ext='')
5065

Aurel's avatar
Aurel committed
5066 5067 5068
      # Export each part
      for item_name in self._item_name_list:
        getattr(self, item_name).export(context=self, bta=bta)
5069

Aurel's avatar
Aurel committed
5070 5071
      return bta.finishCreation()

5072
    security.declareProtected(Permissions.ManagePortal, 'importFile')
Aurel's avatar
Aurel committed
5073 5074
    def importFile(self, dir = 0, file=None, root_path=None):
      """
5075
        Import all xml files in Business Template
Aurel's avatar
Aurel committed
5076 5077 5078 5079 5080 5081 5082
      """
      if dir:
        bta = BusinessTemplateFolder(importing=1, file=file, path=root_path)
      else:
        bta = BusinessTemplateTarball(importing=1, file=file)
      self._portal_type_item = \
          PortalTypeTemplateItem(self.getTemplatePortalTypeIdList())
5083
      self._portal_type_workflow_chain_item = \
5084
          PortalTypeWorkflowChainTemplateItem(self.getTemplatePortalTypeWorkflowChainList())
Aurel's avatar
Aurel committed
5085 5086 5087 5088 5089 5090 5091 5092 5093 5094
      self._workflow_item = \
          WorkflowTemplateItem(self.getTemplateWorkflowIdList())
      self._skin_item = \
          SkinTemplateItem(self.getTemplateSkinIdList())
      self._category_item = \
          CategoryTemplateItem(self.getTemplateBaseCategoryList())
      self._catalog_method_item = \
          CatalogMethodTemplateItem(self.getTemplateCatalogMethodIdList())
      self._action_item = \
          ActionTemplateItem(self.getTemplateActionPathList())
5095 5096
      self._portal_type_roles_item = \
          PortalTypeRolesTemplateItem(self.getTemplatePortalTypeRolesList())
Aurel's avatar
Aurel committed
5097 5098 5099 5100 5101 5102 5103 5104
      self._site_property_item = \
          SitePropertyTemplateItem(self.getTemplateSitePropertyIdList())
      self._module_item = \
          ModuleTemplateItem(self.getTemplateModuleIdList())
      self._document_item = \
          DocumentTemplateItem(self.getTemplateDocumentIdList())
      self._property_sheet_item = \
          PropertySheetTemplateItem(self.getTemplatePropertySheetIdList())
5105 5106
      self._constraint_item = \
          ConstraintTemplateItem(self.getTemplateConstraintIdList())
Aurel's avatar
Aurel committed
5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128
      self._extension_item = \
          ExtensionTemplateItem(self.getTemplateExtensionIdList())
      self._test_item = \
          TestTemplateItem(self.getTemplateTestIdList())
      self._product_item = \
          ProductTemplateItem(self.getTemplateProductIdList())
      self._role_item = \
          RoleTemplateItem(self.getTemplateRoleList())
      self._catalog_result_key_item = \
          CatalogResultKeyTemplateItem(
               self.getTemplateCatalogResultKeyList())
      self._catalog_related_key_item = \
          CatalogRelatedKeyTemplateItem(
               self.getTemplateCatalogRelatedKeyList())
      self._catalog_result_table_item = \
          CatalogResultTableTemplateItem(
               self.getTemplateCatalogResultTableList())
      self._message_translation_item = \
          MessageTranslationTemplateItem(
               self.getTemplateMessageTranslationList())
      self._path_item = \
               PathTemplateItem(self.getTemplatePathList())
5129 5130
      self._preference_item = \
               PreferenceTemplateItem(self.getTemplatePreferenceList())
5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144
      self._portal_type_allowed_content_type_item = \
           PortalTypeAllowedContentTypeTemplateItem(
               self.getTemplatePortalTypeAllowedContentTypeList())
      self._portal_type_hidden_content_type_item = \
           PortalTypeHiddenContentTypeTemplateItem(
               self.getTemplatePortalTypeHiddenContentTypeList())
      self._portal_type_property_sheet_item = \
           PortalTypePropertySheetTemplateItem(
               self.getTemplatePortalTypePropertySheetList())
      self._portal_type_base_category_item = \
           PortalTypeBaseCategoryTemplateItem(
               self.getTemplatePortalTypeBaseCategoryList())
      self._catalog_keyword_key_item = \
          CatalogKeywordKeyTemplateItem(
5145
               self.getTemplateCatalogKeywordKeyList())
5146 5147 5148
      self._catalog_datetime_key_item = \
          CatalogDateTimeKeyTemplateItem(
               self.getTemplateCatalogDatetimeKeyList())
5149 5150
      self._catalog_full_text_key_item = \
          CatalogFullTextKeyTemplateItem(
5151
               self.getTemplateCatalogFullTextKeyList())
5152 5153
      self._catalog_request_key_item = \
          CatalogRequestKeyTemplateItem(
5154
               self.getTemplateCatalogRequestKeyList())
5155 5156
      self._catalog_multivalue_key_item = \
          CatalogMultivalueKeyTemplateItem(
5157
               self.getTemplateCatalogMultivalueKeyList())
5158 5159 5160
      self._catalog_topic_key_item = \
          CatalogTopicKeyTemplateItem(
               self.getTemplateCatalogTopicKeyList())
Aurel's avatar
Aurel committed
5161 5162 5163
      self._local_roles_item = \
          LocalRolesTemplateItem(
               self.getTemplateLocalRolesList())
Yoshinori Okuji's avatar
Yoshinori Okuji committed
5164 5165 5166
      self._tool_item = \
          ToolTemplateItem(
               self.getTemplateToolIdList())
5167 5168 5169
      self._catalog_scriptable_key_item = \
          CatalogScriptableKeyTemplateItem(
               self.getTemplateCatalogScriptableKeyList())
5170 5171 5172 5173 5174 5175
      self._catalog_role_key_item = \
          CatalogRoleKeyTemplateItem(
               self.getTemplateCatalogRoleKeyList())
      self._catalog_local_role_key_item = \
          CatalogLocalRoleKeyTemplateItem(
               self.getTemplateCatalogLocalRoleKeyList())
5176

Aurel's avatar
Aurel committed
5177 5178
      for item_name in self._item_name_list:
        getattr(self, item_name).importFile(bta)
5179

5180 5181 5182 5183 5184
    def getItemsList(self):
      """Return list of items in business template
      """
      items_list = []
      for item_name in self._item_name_list:
5185 5186 5187
        item = getattr(self, item_name, None)
        if item is not None:
          items_list.extend(item.getKeys())
5188
      return items_list
5189

5190 5191 5192 5193 5194 5195 5196 5197 5198 5199
    def checkDependencies(self):
      """
       Check if all the dependencies of the business template
       are installed. Raise an exception with the list of
       missing dependencies if some are missing
      """
      missing_dep_list = []
      dependency_list = self.getDependencyList()
      if len(dependency_list)!=0:
        for dependency_couple in dependency_list:
5200
          dependency_couple_list = dependency_couple.strip().split(' ', 1)
5201
          dependency = dependency_couple_list[0]
5202 5203
          if dependency in (None, ''):
            continue
5204 5205
          version_restriction = None
          if len(dependency_couple_list) > 1:
5206
            version_restriction = dependency_couple_list[1]
5207 5208 5209
            if version_restriction.startswith('('):
              # Something like "(>= 1.0rc6)".
              version_restriction = version_restriction[1:-1]
5210 5211 5212
          installed_bt = self.portal_templates.getInstalledBusinessTemplate(dependency)
          if (not self.portal_templates.IsOneProviderInstalled(dependency)) \
             and ((installed_bt is None) \
5213
                  or (version_restriction not in (None, '') and
5214 5215 5216 5217
                     (not self.portal_templates.compareVersionStrings(installed_bt.getVersion(), version_restriction)))):
            missing_dep_list.append((dependency, version_restriction or ''))
      if len(missing_dep_list) != 0:
        raise BusinessTemplateMissingDependency, 'Impossible to install, please install the following dependencies before: %s'%repr(missing_dep_list)
5218

Aurel's avatar
Aurel committed
5219
    def diffObject(self, REQUEST, **kw):
5220 5221
      """
        Make a diff between an object in the Business Template
5222
        and the same in the Business Template installed in the site
5223 5224 5225 5226
      """

      class_name_dict = {
        'Product' : '_product_item',
5227
        'PropertySheet' : '_property_sheet_item',
5228
        'Constraint' : '_constraint_item',
5229 5230 5231 5232 5233 5234 5235 5236 5237
        'Document' : '_document_item',
        'Extension' : '_extension_item',
        'Test' : '_test_item',
        'Role' : '_role_item',
        'MessageTranslation' : '_message_translation_item',
        'Workflow' : '_workflow_item',
        'CatalogMethod' : '_catalog_method_item',
        'SiteProperty' : '_site_property_item',
        'PortalType' : '_portal_type_item',
5238
        'PortalTypeWorkflowChain' : '_portal_type_workflow_chain_item',
5239 5240 5241 5242
        'PortalTypeAllowedContentType' : '_portal_type_allowed_content_type_item',
        'PortalHiddenAllowedContentType' : '_portal_type_hidden_content_type_item',
        'PortalTypePropertySheet' : '_portal_type_property_sheet_item',
        'PortalTypeBaseCategory' : '_portal_type_base_category_item',
5243 5244 5245 5246
        'Category' : '_category_item',
        'Module' : '_module_item',
        'Skin' : '_skin_item',
        'Path' : '_path_item',
5247
        'Preference' : '_preference_item',
5248
        'Action' : '_action_item',
5249
        'PortalTypeRoles' : '_portal_type_roles_item',
Aurel's avatar
Aurel committed
5250
        'LocalRoles' : '_local_roles_item',
5251 5252 5253
        'CatalogResultKey' : '_catalog_result_key_item',
        'CatalogRelatedKey' : '_catalog_related_key_item',
        'CatalogResultTable' : '_catalog_result_table_item',
5254
        'CatalogKeywordKey' : '_catalog_keyword_key_item',
5255
        'CatalogDateTimeKey' : '_catalog_datetime_key_item',
5256 5257 5258 5259
        'CatalogFullTextKey' : '_catalog_full_text_key_item',
        'CatalogRequestKey' : '_catalog_request_key_item',
        'CatalogMultivalueKey' : '_catalog_multivalue_key_item',
        'CatalogTopicKey' : '_catalog_topic_key_item',
Yoshinori Okuji's avatar
Yoshinori Okuji committed
5260
        'Tool': '_tool_item',
5261
        'CatalogScriptableKey' : '_catalog_scriptable_key_item',
5262 5263
        'CatalogRoleKey' : '_catalog_role_key_item',
        'CatalogLocalRoleKey' : '_catalog_local_role_key_item',
5264
        }
5265

5266 5267
      object_id = REQUEST.object_id
      object_class = REQUEST.object_class
5268

Christophe Dumez's avatar
Christophe Dumez committed
5269
      # Get objects
5270
      item_name = class_name_dict[object_class]
Aurel's avatar
Aurel committed
5271

Aurel's avatar
Aurel committed
5272
      new_bt =self
Christophe Dumez's avatar
Christophe Dumez committed
5273
      # Compare with a given business template
5274
      compare_to_zodb = 0
Aurel's avatar
Aurel committed
5275 5276
      bt2_id = kw.get('compare_with', None)
      if bt2_id is not None:
5277 5278
        if bt2_id == self.getId():
          compare_to_zodb = 1
5279
          installed_bt = self.getInstalledBusinessTemplate(title=self.getTitle())
5280 5281
        else:
          installed_bt = self.portal_templates._getOb(bt2_id)
Aurel's avatar
Aurel committed
5282 5283 5284
      else:
        installed_bt = self.getInstalledBusinessTemplate(title=self.getTitle())
        if installed_bt == new_bt:
Aurel's avatar
Aurel committed
5285 5286
          compare_to_zodb = 1
      if compare_to_zodb:
5287
        bt2 = self.portal_templates.manage_clone(ob=installed_bt, id=INSTALLED_BT_FOR_DIFF)
Christophe Dumez's avatar
Christophe Dumez committed
5288
        # Update portal types properties to get last modifications
Aurel's avatar
Aurel committed
5289 5290 5291
        bt2.getPortalTypesProperties()
        bt2.edit(description='tmp bt generated for diff')
        installed_bt = bt2
5292

5293 5294
      new_item = getattr(new_bt, item_name)
      installed_item = getattr(installed_bt, item_name)
5295
      if compare_to_zodb:
Aurel's avatar
Aurel committed
5296
        # XXX maybe only build for the given object to gain time
5297
        installed_item.build(self)
5298 5299 5300
      new_object = new_item._objects[object_id]
      installed_object = installed_item._objects[object_id]
      diff_msg = ''
5301

Christophe Dumez's avatar
Christophe Dumez committed
5302
      # Real Zope Objects (can be exported into XML directly by Zope)
5303 5304
      # XXX Bad naming
      item_list_1 = ['_product_item', '_workflow_item', '_portal_type_item',
5305
                     '_category_item', '_path_item', '_preference_tem',
Yoshinori Okuji's avatar
Yoshinori Okuji committed
5306
                     '_skin_item', '_action_item', '_tool_item', ]
5307

Christophe Dumez's avatar
Christophe Dumez committed
5308
      # Not considered as objects by Zope (will be exported into XML manually)
5309
      # XXX Bad naming
5310
      item_list_2 = ['_site_property_item', '_module_item',
5311
                     '_catalog_result_key_item', '_catalog_related_key_item',
5312 5313
                     '_catalog_result_table_item',
                     '_catalog_keyword_key_item',
5314
                     '_catalog_datetime_key_item',
5315 5316 5317 5318
                     '_catalog_full_text_key_item',
                     '_catalog_request_key_item',
                     '_catalog_multivalue_key_item',
                     '_catalog_topic_key_item',
5319
                     '_catalog_scriptable_key_item',
5320 5321
                     '_catalog_role_key_item',
                     '_catalog_local_role_key_item',
5322 5323 5324 5325 5326 5327
                     '_portal_type_allowed_content_type_item',
                     '_portal_type_hidden_content_type_item',
                     '_portal_type_property_sheet_item',
                     '_portal_type_roles_item',
                     '_portal_type_base_category_item',
                     '_local_roles_item',
5328
                     '_portal_type_workflow_chain_item',]
5329

Christophe Dumez's avatar
Christophe Dumez committed
5330
      # Text objects (no need to export them into XML)
5331
      # XXX Bad naming
5332 5333
      item_list_3 = ['_document_item', '_property_sheet_item',
                     '_constraint_item', '_extension_item',
5334
                     '_test_item', '_message_translation_item',]
5335

5336
      if item_name in item_list_1:
Christophe Dumez's avatar
Christophe Dumez committed
5337 5338 5339
        f1 = StringIO() # for XML export of New Object
        f2 = StringIO() # For XML export of Installed Object
        # Remove unneeded properties
5340 5341
        new_object = new_item.removeProperties(new_object)
        installed_object = installed_item.removeProperties(installed_object)
5342
        # XML Export in memory
5343 5344 5345
        OFS.XMLExportImport.exportXML(new_object._p_jar, new_object._p_oid, f1)
        OFS.XMLExportImport.exportXML(installed_object._p_jar, installed_object._p_oid, f2)
        new_obj_xml = f1.getvalue()
5346
        f1.close()
5347 5348 5349 5350
        installed_obj_xml = f2.getvalue()
        f2.close()
        new_ob_xml_lines = new_obj_xml.splitlines()
        installed_ob_xml_lines = installed_obj_xml.splitlines()
5351
        # End of XML export
5352

5353
        # Diff between XML objects
5354 5355
        diff_list = list(unified_diff(installed_ob_xml_lines, new_ob_xml_lines, tofile=new_bt.getId(), fromfile=installed_bt.getId(), lineterm=''))
        if len(diff_list) != 0:
Christophe Dumez's avatar
Christophe Dumez committed
5356
          diff_msg += '\n\nObject %s diff :\n' % (object_id,)
5357 5358 5359
          diff_msg += '\n'.join(diff_list)
        else:
          diff_msg = 'No diff'
5360

5361
      elif item_name in item_list_2:
5362
        # Generate XML code manually
5363 5364
        new_obj_xml = new_item.generateXml(path= object_id)
        installed_obj_xml = installed_item.generateXml(path= object_id)
5365 5366
        new_obj_xml_lines = new_obj_xml.splitlines()
        installed_obj_xml_lines = installed_obj_xml.splitlines()
5367
        # End of XML Code Generation
5368

5369
        # Diff between XML objects
5370 5371
        diff_list = list(unified_diff(installed_obj_xml_lines, new_obj_xml_lines, tofile=new_bt.getId(), fromfile=installed_bt.getId(), lineterm=''))
        if len(diff_list) != 0:
Christophe Dumez's avatar
Christophe Dumez committed
5372
          diff_msg += '\n\nObject %s diff :\n' % (object_id,)
5373 5374 5375
          diff_msg += '\n'.join(diff_list)
        else:
          diff_msg = 'No diff'
5376

5377
      elif item_name in item_list_3:
5378
        # Diff between text objects
5379 5380 5381 5382
        new_obj_lines = new_object.splitlines()
        installed_obj_lines = installed_object.splitlines()
        diff_list = list(unified_diff(installed_obj_lines, new_obj_lines, tofile=new_bt.getId(), fromfile=installed_bt.getId(), lineterm=''))
        if len(diff_list) != 0:
Christophe Dumez's avatar
Christophe Dumez committed
5383
          diff_msg += '\n\nObject %s diff :\n' % (object_id,)
5384 5385
          diff_msg += '\n'.join(diff_list)
        else:
5386
          diff_msg = 'No diff'
5387

5388
      else:
5389
        diff_msg += 'Unsupported file !'
Christophe Dumez's avatar
Christophe Dumez committed
5390

5391
      if compare_to_zodb:
5392
        self.portal_templates.manage_delObjects(ids=[INSTALLED_BT_FOR_DIFF])
5393

5394
      return diff_msg
5395

5396

5397 5398 5399 5400
    def getPortalTypesProperties(self, **kw):
      """
      Fill field about properties for each portal type
      """
5401 5402
      wtool = self.getPortalObject().portal_workflow
      ttool = self.getPortalObject().portal_types
5403 5404 5405 5406 5407 5408 5409
      bt_allowed_content_type_list = list(getattr(self, 'template_portal_type_allowed_content_type', []) or [])
      bt_hidden_content_type_list = list(getattr(self, 'template_portal_type_hidden_content_type', []) or [])
      bt_property_sheet_list = list(getattr(self, 'template_portal_type_property_sheet', []) or [])
      bt_base_category_list = list(getattr(self, 'template_portal_type_base_category', []) or [])
      bt_action_list = list(getattr(self, 'template_action_path', []) or [])
      bt_portal_types_id_list = list(self.getTemplatePortalTypeIdList())
      bt_portal_type_roles_list =  list(getattr(self, 'template_portal_type_roles', []) or [])
5410 5411
      bt_wf_chain_list = list(getattr(self, 'template_portal_type_workflow_chain', []) or [])

5412
      p = self.getPortalObject()
5413
      for id in bt_portal_types_id_list:
5414 5415
        portal_type = ttool.getTypeInfo(id)
        if portal_type is None:
5416
          continue
5417
        if len(getattr(portal_type, '_roles', ())) > 0:
5418 5419
          if id not in bt_portal_type_roles_list:
            bt_portal_type_roles_list.append(id)
5420

5421 5422 5423 5424
        allowed_content_type_list = []
        hidden_content_type_list = []
        property_sheet_list = []
        base_category_list = []
5425
        action_list = []
5426 5427 5428 5429 5430 5431 5432
        if hasattr(portal_type, 'allowed_content_types'):
          allowed_content_type_list = portal_type.allowed_content_types
        if hasattr(portal_type, 'hidden_content_type_list'):
          hidden_content_type_list = portal_type.hidden_content_type_list
        if hasattr(portal_type, 'property_sheet_list'):
          property_sheet_list = portal_type.property_sheet_list
        if hasattr(portal_type, 'base_category_list'):
5433
          base_category_list = portal_type.base_category_list
5434 5435
        if hasattr(portal_type, 'listActions'):
          action_list = [x.getId() for x in portal_type.listActions()]
5436

5437 5438 5439 5440
        for a_id in allowed_content_type_list:
          allowed_id = id+' | '+a_id
          if allowed_id not in bt_allowed_content_type_list:
            bt_allowed_content_type_list.append(allowed_id)
5441

5442
        for h_id in hidden_content_type_list:
5443 5444 5445
          hidden_id = id+' | '+h_id
          if hidden_id not in bt_hidden_content_type_list:
            bt_hidden_content_type_list.append(hidden_id)
5446

5447 5448 5449 5450
        for ps_id in property_sheet_list:
          p_sheet_id = id+' | '+ps_id
          if p_sheet_id not in bt_property_sheet_list:
            bt_property_sheet_list.append(p_sheet_id)
5451

5452 5453 5454 5455
        for bc_id in base_category_list:
          base_cat_id = id+' | '+bc_id
          if base_cat_id not in bt_base_category_list:
            bt_base_category_list.append(base_cat_id)
5456

5457 5458 5459 5460
        for act_id in action_list:
          action_id = id+' | '+act_id
          if action_id not in bt_action_list:
            bt_action_list.append(action_id)
5461

5462
        for workflow_id in [chain for chain in wtool.getChainFor(id)
5463
                                    if chain != '(Default)']:
5464 5465 5466
          wf_id = id+' | '+workflow_id
          if wf_id not in bt_wf_chain_list:
            bt_wf_chain_list.append(wf_id)
5467

5468 5469 5470 5471
      bt_allowed_content_type_list.sort()
      bt_hidden_content_type_list.sort()
      bt_property_sheet_list.sort()
      bt_base_category_list.sort()
5472
      bt_action_list.sort()
5473
      bt_wf_chain_list.sort()
5474

5475 5476 5477 5478 5479 5480 5481
      self.setProperty('template_portal_type_workflow_chain', bt_wf_chain_list)
      self.setProperty('template_portal_type_roles', bt_portal_type_roles_list)
      self.setProperty('template_portal_type_allowed_content_type', bt_allowed_content_type_list)
      self.setProperty('template_portal_type_hidden_content_type', bt_hidden_content_type_list)
      self.setProperty('template_portal_type_property_sheet', bt_property_sheet_list)
      self.setProperty('template_portal_type_base_category', bt_base_category_list)
      self.setProperty('template_action_path', bt_action_list)
5482 5483 5484 5485 5486 5487 5488 5489 5490
      return


    def guessPortalTypes(self, **kw):
      """
      This method guesses portal types based on modules define in the Business Template
      """
      bt_module_id_list = list(self.getTemplateModuleIdList())
      if len(bt_module_id_list) == 0:
5491 5492
        raise TemplateConditionError, 'No module defined in business template'

5493 5494 5495 5496 5497 5498 5499 5500
      bt_portal_types_id_list = list(self.getTemplatePortalTypeIdList())

      def getChildPortalType(type_id):
        type_list = {}
        p = self.getPortalObject()
        try:
          portal_type = p.unrestrictedTraverse('portal_types/'+type_id)
        except KeyError:
5501
          return type_list
5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515

        allowed_content_type_list = []
        hidden_content_type_list = []
        if hasattr(portal_type, 'allowed_content_types'):
          allowed_content_type_list = portal_type.allowed_content_types
        if hasattr(portal_type, 'hidden_content_type_list'):
          hidden_content_type_list = portal_type.hidden_content_type_list
        type_list[type_id] = ()
        # get same info for allowed portal types and hidden portal types
        for allowed_ptype_id in allowed_content_type_list:
          if allowed_ptype_id not in type_list.keys():
            type_list.update(getChildPortalType(allowed_ptype_id))
        for hidden_ptype_id in hidden_content_type_list:
          if hidden_ptype_id not in type_list.keys():
5516
            type_list.update(getChildPortalType(hidden_ptype_id))
5517
        return type_list
5518

5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565
      p = self.getPortalObject()
      portal_dict = {}
      for module_id in bt_module_id_list:
        module = p.unrestrictedTraverse(module_id)
        portal_type_id = module.getPortalType()
        try:
          portal_type = p.unrestrictedTraverse('portal_types/'+portal_type_id)
        except KeyError:
          continue
        allowed_content_type_list = []
        hidden_content_type_list = []
        if hasattr(portal_type, 'allowed_content_types'):
          allowed_content_type_list = portal_type.allowed_content_types
        if hasattr(portal_type, 'hidden_content_type_list'):
          hidden_content_type_list = portal_type.hidden_content_type_list

        portal_dict[portal_type_id] = ()

        for allowed_type_id in allowed_content_type_list:
          if allowed_type_id not in portal_dict.keys():
            portal_dict.update(getChildPortalType(allowed_type_id))

        for hidden_type_id in hidden_content_type_list:
          if hidden_type_id not in portal_dict.keys():
            portal_dict.update(getChildPortalType(hidden_type_id))

      # construct portal type list, keep already present portal types
      for id in portal_dict.keys():
        if id not in bt_portal_types_id_list:
          bt_portal_types_id_list.append(id)

      bt_portal_types_id_list.sort()

      setattr(self, 'template_portal_type_id', bt_portal_types_id_list)
      return

    def clearPortalTypes(self, **kw):
      """
      clear id list register for portal types
      """
      setattr(self, 'template_portal_type_id', ())
      setattr(self, 'template_portal_type_allowed_content_type', ())
      setattr(self, 'template_portal_type_hidden_content_type', ())
      setattr(self, 'template_portal_type_property_sheet', ())
      setattr(self, 'template_portal_type_base_category', ())
      return

5566 5567 5568
# Block acquisition on all _item_name_list properties by setting
# a default class value to None
for key in BusinessTemplate._item_name_list:
5569
  setattr(BusinessTemplate, key, None)
5570

5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597
# Transaction Manager used for update of business template workflow
# XXX update seems to works without it

# from Shared.DC.ZRDB.TM import TM

# class WorkflowUpdateTM(TM):

#   _p_oid=_p_changed=_registered=None
#   _update = 0

#   def __init__(self, ):
#     LOG('init TM', 0, '')

#   def register(self, update=0, gen=None, site=None):
#     LOG('register TM', 0, update)
#     self._gen = gen
#     self._site = site
#     self._update = update
#     self._register()

#   def tpc_prepare(self, *d, **kw):
#     LOG("tpc_prepare", 0, self._update)
#     if self._update:
#       # do it one time
#       self._update = 0
#       LOG('call update of wf', 0, '')
#       self._gen.setupWorkflow(self._site)
5598

5599 5600 5601 5602 5603 5604 5605 5606

#   def _finish(self, **kw):
#     LOG('finish TM', 0, '')
#     pass

#   def _abort(self, **kw):
#     LOG('abort TM', 0, '')
#     pass
5607