Commit a462aa1c authored by Julien Muchembled's avatar Julien Muchembled

When uninstalling document item, restore document class from product if any

This fixes TestBusinessTemplate.test_168_DocumentUninstallIsEffective

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@39835 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 92495f23
...@@ -3380,6 +3380,21 @@ class DocumentTemplateItem(BaseTemplateItem): ...@@ -3380,6 +3380,21 @@ class DocumentTemplateItem(BaseTemplateItem):
{path : ['Removed', self.__class__.__name__[:-12]]}) {path : ['Removed', self.__class__.__name__[:-12]]})
return modified_object_list return modified_object_list
def _resetDynamicModules(self):
# before any import, flush all ZODB caches to force a DB reload
# otherwise we could have objects trying to get commited while
# holding reference to a class that is no longer the same one as
# the class in its import location and pickle doesn't tolerate it.
# First we do a savepoint to dump dirty objects to temporary
# storage, so that all references to them can be freed.
transaction.savepoint(optimistic=True)
# Then we need to flush from all caches, not only the one from this
# connection
portal = self.getPortalObject()
portal._p_jar.db().cacheMinimize()
synchronizeDynamicModules(portal, force=True)
gc.collect()
def install(self, context, trashbin, **kw): def install(self, context, trashbin, **kw):
update_dict = kw.get('object_to_update') update_dict = kw.get('object_to_update')
force = kw.get('force') force = kw.get('force')
...@@ -3393,7 +3408,6 @@ class DocumentTemplateItem(BaseTemplateItem): ...@@ -3393,7 +3408,6 @@ class DocumentTemplateItem(BaseTemplateItem):
continue continue
text = self._objects[id] text = self._objects[id]
path, name = posixpath.split(id) path, name = posixpath.split(id)
# This raises an exception if the file already exists.
try: try:
self.local_file_writer_name(name, text, create=0) self.local_file_writer_name(name, text, create=0)
except IOError, error: except IOError, error:
...@@ -3405,19 +3419,7 @@ class DocumentTemplateItem(BaseTemplateItem): ...@@ -3405,19 +3419,7 @@ class DocumentTemplateItem(BaseTemplateItem):
if self.local_file_importer_name is None: if self.local_file_importer_name is None:
continue continue
if need_reset: if need_reset:
# before any import, flush all ZODB caches to force a DB reload self._resetDynamicModules()
# otherwise we could have objects trying to get commited while
# holding reference to a class that is no longer the same one as
# the class in its import location and pickle doesn't tolerate it.
# First we do a savepoint to dump dirty objects to temporary
# storage, so that all references to them can be freed.
transaction.savepoint(optimistic=True)
# Then we need to flush from all caches, not only the one from this
# connection
portal = self.getPortalObject()
portal._p_jar.db().cacheMinimize()
synchronizeDynamicModules(portal, force=True)
gc.collect()
need_reset = False need_reset = False
self.local_file_importer_name(name) self.local_file_importer_name(name)
else: else:
...@@ -3435,8 +3437,11 @@ class DocumentTemplateItem(BaseTemplateItem): ...@@ -3435,8 +3437,11 @@ class DocumentTemplateItem(BaseTemplateItem):
object_keys = [object_path] object_keys = [object_path]
else: else:
object_keys = self._archive.keys() object_keys = self._archive.keys()
for key in object_keys: if object_keys:
self.local_file_remover_name(key) if isinstance(self, DocumentTemplateItem):
self._resetDynamicModules()
for key in object_keys:
self.local_file_remover_name(key)
BaseTemplateItem.uninstall(self, context, **kw) BaseTemplateItem.uninstall(self, context, **kw)
def export(self, context, bta, **kw): def export(self, context, bta, **kw):
......
...@@ -6712,7 +6712,6 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): ...@@ -6712,7 +6712,6 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor):
# check the previously existing instance now behaves as the overriden class # check the previously existing instance now behaves as the overriden class
self.assertTrue(getattr(portal.another_file, 'isClassOverriden', False)) self.assertTrue(getattr(portal.another_file, 'isClassOverriden', False))
@expectedFailure
def test_168_DocumentUninstallIsEffective(self): def test_168_DocumentUninstallIsEffective(self):
portal = self.portal portal = self.portal
# Test_167 above needs to have been run # Test_167 above needs to have been run
...@@ -6720,11 +6719,8 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): ...@@ -6720,11 +6719,8 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor):
'isClassOverriden', 'isClassOverriden',
False): False):
self.test_167_InstanceAndRelatedClassDefinedInSameBT() self.test_167_InstanceAndRelatedClassDefinedInSameBT()
self.uninstallBusinessTemplate('test_data') self.uninstallBusinessTemplate('test_bt')
transaction.commit()
self.tic()
# check both File instances no longer behave like being overriden # check both File instances no longer behave like being overriden
self.assertFalse(getattr(portal.some_file, 'isClassOverriden', False))
self.assertFalse(getattr(portal.another_file, 'isClassOverriden', False)) self.assertFalse(getattr(portal.another_file, 'isClassOverriden', False))
def test_getBusinessTemplateUrl(self): def test_getBusinessTemplateUrl(self):
......
...@@ -30,46 +30,39 @@ ...@@ -30,46 +30,39 @@
import os, re, string, sys import os, re, string, sys
from Products.ERP5Type import document_class_registry
from Products.ERP5Type.Globals import package_home, InitializeClass from Products.ERP5Type.Globals import package_home, InitializeClass
from zLOG import LOG from zLOG import LOG
global product_document_registry product_document_registry = {}
product_document_registry = []
global product_interactor_registry
product_interactor_registry = [] product_interactor_registry = []
global interactor_class_id_registry
interactor_class_id_registry = {} interactor_class_id_registry = {}
def getProductDocumentPathList(): def getProductDocumentPathList():
result = product_document_registry return sorted((k, os.path.dirname(sys.modules[v.rsplit('.', 1)[0]].__file__))
result.sort() for k,v in product_document_registry.iteritems())
return result
def InitializeDocument(document_class, document_path=None): def InitializeDocument(class_id, class_path):
global product_document_registry
# Register class in ERP5Type.Document # Register class in ERP5Type.Document
product_document_registry.append(((document_class, document_path))) product_document_registry[class_id] = class_path
def getProductInteractorPathList(): def getProductInteractorPathList():
result = product_interactor_registry return sorted(product_interactor_registry)
result.sort()
return result
def InitializeInteractor(interactor_class, interactor_path=None): def InitializeInteractor(interactor_class, interactor_path=None):
global product_interactor_registry
# Register class in ERP5Type.Interactor # Register class in ERP5Type.Interactor
product_interactor_registry.append(((interactor_class, interactor_path))) product_interactor_registry.append(((interactor_class, interactor_path)))
def initializeProductDocumentRegistry(): def initializeProductDocumentRegistry():
from Utils import importLocalDocument from Utils import importLocalDocument
for (class_id, document_path) in product_document_registry: for (class_id, class_path) in product_document_registry.iteritems():
importLocalDocument(class_id, path=document_path) importLocalDocument(class_id, class_path=class_path)
#from Testing import ZopeTestCase #from Testing import ZopeTestCase
#ZopeTestCase._print('Added product document to ERP5Type repository: %s (%s) \n' % (class_id, document_path)) #ZopeTestCase._print('Added product document to ERP5Type repository: %s (%s) \n' % (class_id, document_path))
#LOG('Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path), 0, '') #LOG('Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path), 0, '')
#print 'Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path) #print 'Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path)
def initializeProductInteractorRegistry(): def initializeProductInteractorRegistry():
from Utils import importLocalInteractor from Utils import importLocalInteractor
for (class_id, interactor_path) in product_interactor_registry: for (class_id, interactor_path) in product_interactor_registry:
...@@ -77,10 +70,8 @@ def initializeProductInteractorRegistry(): ...@@ -77,10 +70,8 @@ def initializeProductInteractorRegistry():
importLocalInteractor(class_id, path=interactor_path) importLocalInteractor(class_id, path=interactor_path)
def registerInteractorClass(class_id, klass): def registerInteractorClass(class_id, klass):
global interactor_class_id_registry
interactor_class_id_registry[class_id] = klass interactor_class_id_registry[class_id] = klass
def installInteractorClassRegistry(): def installInteractorClassRegistry():
global interactor_class_id_registry for klass in interactor_class_id_registry.itervalues():
for class_id, klass in interactor_class_id_registry.items():
klass().install() klass().install()
...@@ -87,7 +87,7 @@ def simple_decorator(decorator): ...@@ -87,7 +87,7 @@ def simple_decorator(decorator):
return new_decorator return new_decorator
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type import document_class_registry
from Products.ERP5Type.Accessor.Constant import PropertyGetter as \ from Products.ERP5Type.Accessor.Constant import PropertyGetter as \
PropertyConstantGetter PropertyConstantGetter
from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
...@@ -470,7 +470,6 @@ def updateGlobals(this_module, global_hook, ...@@ -470,7 +470,6 @@ def updateGlobals(this_module, global_hook,
for module_id in module_id_list: for module_id in module_id_list:
import_method(module_id, path=path, is_erp5_type=is_erp5_type) import_method(module_id, path=path, is_erp5_type=is_erp5_type)
from Products.ERP5Type import document_class_registry
module_name = this_module.__name__ module_name = this_module.__name__
# Return core document_class list (for ERP5Type only) # Return core document_class list (for ERP5Type only)
# this was introduced to permit overriding ERP5Type Document classes # this was introduced to permit overriding ERP5Type Document classes
...@@ -478,14 +477,12 @@ def updateGlobals(this_module, global_hook, ...@@ -478,14 +477,12 @@ def updateGlobals(this_module, global_hook,
path, core_module_id_list = getModuleIdList(product_path, 'Core') path, core_module_id_list = getModuleIdList(product_path, 'Core')
for document in core_module_id_list: for document in core_module_id_list:
module_path = '.'.join((module_name, 'Core', document, document)) module_path = '.'.join((module_name, 'Core', document, document))
document_class_registry[document] = module_path InitializeDocument(document, module_path)
InitializeDocument(document, document_path=path)
# Return document_class list # Return document_class list
path, module_id_list = getModuleIdList(product_path, 'Document') path, module_id_list = getModuleIdList(product_path, 'Document')
for document in module_id_list: for document in module_id_list:
module_path = '.'.join((module_name, 'Document', document, document)) module_path = '.'.join((module_name, 'Document', document, document))
document_class_registry[document] = module_path InitializeDocument(document, module_path)
InitializeDocument(document, document_path=path)
# Return interactor_class list # Return interactor_class list
path, interactor_id_list = getModuleIdList(product_path, 'Interactor') path, interactor_id_list = getModuleIdList(product_path, 'Interactor')
...@@ -803,6 +800,14 @@ def removeLocalDocument(class_id): ...@@ -803,6 +800,14 @@ def removeLocalDocument(class_id):
f = os.path.join(path, "%s.%s" % (class_id, ext)) f = os.path.join(path, "%s.%s" % (class_id, ext))
if os.path.exists(f): if os.path.exists(f):
os.remove(f) os.remove(f)
if document_class_registry.pop(class_id, None):
# restore original class (from product) if any
from Products.ERP5Type.InitGenerator import product_document_registry
product_path = product_document_registry.get(class_id)
if product_path:
importLocalDocument(class_id, class_path=product_path)
else:
pass # XXX Do we need to clean up ?
def readLocalDocument(class_id): def readLocalDocument(class_id):
instance_home = getConfiguration().instancehome instance_home = getConfiguration().instancehome
...@@ -826,22 +831,14 @@ def writeLocalDocument(class_id, text, create=1, instance_home=None): ...@@ -826,22 +831,14 @@ def writeLocalDocument(class_id, text, create=1, instance_home=None):
if create: if create:
if os.path.exists(path): if os.path.exists(path):
raise IOError, 'the file %s is already present' % path raise IOError, 'the file %s is already present' % path
# check there is no syntax error (that's the most we can do at this time)
compile(text, path, 'exec')
f = open(path, 'w') f = open(path, 'w')
try: try:
f.write(text) f.write(text)
finally: finally:
f.close() f.close()
module_path = "erp5.document"
classpath = "%s.%s" % (module_path, class_id)
module = imp.load_source(classpath, path)
import erp5.document
setattr(erp5.document, class_id, getattr(module, class_id))
# and register correctly the new document
from Products.ERP5Type import document_class_registry
document_class_registry[class_id] = classpath
def setDefaultClassProperties(property_holder): def setDefaultClassProperties(property_holder):
"""Initialize default properties for ERP5Type Documents. """Initialize default properties for ERP5Type Documents.
""" """
...@@ -917,17 +914,16 @@ class PersistentMigrationMixin(object): ...@@ -917,17 +914,16 @@ class PersistentMigrationMixin(object):
from Globals import Persistent, PersistentMapping from Globals import Persistent, PersistentMapping
def importLocalDocument(class_id, path=None): def importLocalDocument(class_id, path=None, class_path=None):
"""Imports a document class and registers it in ERP5Type Document """Imports a document class and registers it in ERP5Type Document
repository ( Products.ERP5Type.Document ) repository ( Products.ERP5Type.Document )
""" """
import Products.ERP5Type.Document import Products.ERP5Type.Document
import Permissions import Permissions
from Products.ERP5Type import document_class_registry if class_path:
if path and 'products' in path.lower(): # XXX assert path is None
classpath = document_class_registry[class_id] module_path = class_path.rsplit('.', 1)[0]
module_path = classpath.rsplit('.', 1)[0]
module = __import__(module_path, {}, {}, (module_path,)) module = __import__(module_path, {}, {}, (module_path,))
else: else:
# local document in INSTANCE_HOME/Document/ # local document in INSTANCE_HOME/Document/
...@@ -937,11 +933,11 @@ def importLocalDocument(class_id, path=None): ...@@ -937,11 +933,11 @@ def importLocalDocument(class_id, path=None):
path = os.path.join(instance_home, "Document") path = os.path.join(instance_home, "Document")
path = os.path.join(path, "%s.py" % class_id) path = os.path.join(path, "%s.py" % class_id)
module_path = "erp5.document" module_path = "erp5.document"
classpath = "%s.%s" % (module_path, class_id) class_path = "%s.%s" % (module_path, class_id)
module = imp.load_source(classpath, path) module = imp.load_source(class_path, path)
import erp5.document import erp5.document
setattr(erp5.document, class_id, getattr(module, class_id)) setattr(erp5.document, class_id, getattr(module, class_id))
document_class_registry[class_id] = classpath document_class_registry[class_id] = class_path
### Migration ### Migration
module_name = "Products.ERP5Type.Document.%s" % class_id module_name = "Products.ERP5Type.Document.%s" % class_id
...@@ -986,7 +982,6 @@ def importLocalDocument(class_id, path=None): ...@@ -986,7 +982,6 @@ def importLocalDocument(class_id, path=None):
# XXX really? # XXX really?
return klass, tuple() return klass, tuple()
def initializeLocalRegistry(directory_name, import_local_method): def initializeLocalRegistry(directory_name, import_local_method):
""" """
Initialize local directory. Initialize local directory.
...@@ -1049,7 +1044,6 @@ def initializeProduct( context, ...@@ -1049,7 +1044,6 @@ def initializeProduct( context,
""" """
module_name = this_module.__name__ module_name = this_module.__name__
from Products.ERP5Type import document_class_registry
# Content classes are exceptions and should be registered here. # Content classes are exceptions and should be registered here.
# other products were all already registered in updateGlobals() # other products were all already registered in updateGlobals()
# because getModuleIdList works fine for Document/ and Core/ # because getModuleIdList works fine for Document/ and Core/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment