Commit d8961c51 authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: Allow migration of {Interface,Mixin,Module,Tool} from...

ZODB Components: Allow migration of {Interface,Mixin,Module,Tool} from Business Template UI and on all Products (not only Products.ERP5).

This introduces the following new ZODB Components:
  + Module Component: Non-Documents/non-persistent classes of modules usually
    found at the top-level of Products (eg Products.ERP5.XXX) on FS. Considering
    that all other Components types are actually Modules, make it the base class.
  + Tool Component: Tool directory of Products on FS (eg Products.ERP5.Tool.XXX).
    => DiffTool and CallableTool are now 'Tool Component' instead of plain
       'Document Component' and properly registered as Tools like FS Products Tool.

Skip CMFActivity and HBTreeFolder2 Products for now in migration View for now as
almost many Portal Type classes have ActiveObject or HBTreeFolder2 in their MRO
and these Products will be done at the end anyway...
parent a4e63da9
...@@ -89,6 +89,7 @@ from urllib import quote, unquote ...@@ -89,6 +89,7 @@ from urllib import quote, unquote
from difflib import unified_diff from difflib import unified_diff
import posixpath import posixpath
import transaction import transaction
import inspect
import threading import threading
from ZODB.broken import Broken, BrokenModified from ZODB.broken import Broken, BrokenModified
...@@ -138,6 +139,7 @@ SEPARATELY_EXPORTED_PROPERTY_DICT = { ...@@ -138,6 +139,7 @@ SEPARATELY_EXPORTED_PROPERTY_DICT = {
"Interface Component": ("py", 0, "text_content"), "Interface Component": ("py", 0, "text_content"),
"OOoTemplate": ("oot", 1, "_text"), "OOoTemplate": ("oot", 1, "_text"),
"Mixin Component": ("py", 0, "text_content"), "Mixin Component": ("py", 0, "text_content"),
"Module Component": ("py", 0, "text_content"),
"PDF": ("pdf", 0, "data"), "PDF": ("pdf", 0, "data"),
"PDFForm": ("pdf", 0, "data"), "PDFForm": ("pdf", 0, "data"),
"PyData Script": ("py", 0, "_body"), "PyData Script": ("py", 0, "_body"),
...@@ -148,6 +150,7 @@ SEPARATELY_EXPORTED_PROPERTY_DICT = { ...@@ -148,6 +150,7 @@ SEPARATELY_EXPORTED_PROPERTY_DICT = {
"SQL Method": ("sql", 0, "src"), "SQL Method": ("sql", 0, "src"),
"Test Component": ("py", 0, "text_content"), "Test Component": ("py", 0, "text_content"),
"Test Page": (None, 0, "text_content"), "Test Page": (None, 0, "text_content"),
"Tool Component": ("py", 0, "text_content"),
"Web Page": (None, 0, "text_content"), "Web Page": (None, 0, "text_content"),
"Web Script": (None, 0, "text_content"), "Web Script": (None, 0, "text_content"),
"Web Style": (None, 0, "text_content"), "Web Style": (None, 0, "text_content"),
...@@ -4262,6 +4265,12 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem): ...@@ -4262,6 +4265,12 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem):
self.portal_components.reset(force=True, self.portal_components.reset(force=True,
reset_portal_type_at_transaction_boundary=True) reset_portal_type_at_transaction_boundary=True)
from Products.ERP5Type.Core.ModuleComponent import ModuleComponent
class ModuleComponentTemplateItem(_ZodbComponentTemplateItem):
@staticmethod
def _getZodbObjectId(id):
return ModuleComponent.getIdPrefix() + '.' + id
from Products.ERP5Type.Core.InterfaceComponent import InterfaceComponent from Products.ERP5Type.Core.InterfaceComponent import InterfaceComponent
class InterfaceTemplateItem(_ZodbComponentTemplateItem): class InterfaceTemplateItem(_ZodbComponentTemplateItem):
@staticmethod @staticmethod
...@@ -4274,6 +4283,12 @@ class MixinTemplateItem(_ZodbComponentTemplateItem): ...@@ -4274,6 +4283,12 @@ class MixinTemplateItem(_ZodbComponentTemplateItem):
def _getZodbObjectId(id): def _getZodbObjectId(id):
return MixinComponent.getIdPrefix() + '.' + id return MixinComponent.getIdPrefix() + '.' + id
from Products.ERP5Type.Core.ToolComponent import ToolComponent
class ToolComponentTemplateItem(_ZodbComponentTemplateItem):
@staticmethod
def _getZodbObjectId(id):
return ToolComponent.getIdPrefix() + '.' + id
from Products.ERP5Type.Core.DocumentComponent import DocumentComponent from Products.ERP5Type.Core.DocumentComponent import DocumentComponent
class DocumentTemplateItem(FilesystemToZodbTemplateItem, class DocumentTemplateItem(FilesystemToZodbTemplateItem,
_ZodbComponentTemplateItem): _ZodbComponentTemplateItem):
...@@ -4370,7 +4385,6 @@ class DocumentTemplateItem(FilesystemToZodbTemplateItem, ...@@ -4370,7 +4385,6 @@ class DocumentTemplateItem(FilesystemToZodbTemplateItem,
afterUninstall = _ZodbComponentTemplateItem.afterUninstall afterUninstall = _ZodbComponentTemplateItem.afterUninstall
from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent
class ExtensionTemplateItem(DocumentTemplateItem): class ExtensionTemplateItem(DocumentTemplateItem):
""" """
Extensions are now stored in ZODB rather than on the filesystem. However, Extensions are now stored in ZODB rather than on the filesystem. However,
...@@ -5243,10 +5257,14 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5243,10 +5257,14 @@ Business Template is a set of definitions, such as skins, portal types and categ
self._catalog_security_uid_column_item = \ self._catalog_security_uid_column_item = \
CatalogSecurityUidColumnTemplateItem( CatalogSecurityUidColumnTemplateItem(
self.getTemplateCatalogSecurityUidColumnList()) self.getTemplateCatalogSecurityUidColumnList())
self._module_component_item = \
ModuleComponentTemplateItem(self.getTemplateModuleComponentIdList())
self._interface_item = \ self._interface_item = \
InterfaceTemplateItem(self.getTemplateInterfaceIdList()) InterfaceTemplateItem(self.getTemplateInterfaceIdList())
self._mixin_item = \ self._mixin_item = \
MixinTemplateItem(self.getTemplateMixinIdList()) MixinTemplateItem(self.getTemplateMixinIdList())
self._tool_component_item = \
ToolComponentTemplateItem(self.getTemplateToolComponentIdList())
security.declareProtected(Permissions.ManagePortal, 'build') security.declareProtected(Permissions.ManagePortal, 'build')
def build(self, no_action=0, update_revision=True): def build(self, no_action=0, update_revision=True):
...@@ -5982,9 +6000,11 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5982,9 +6000,11 @@ Business Template is a set of definitions, such as skins, portal types and categ
'Product' : '_product_item', 'Product' : '_product_item',
'PropertySheet' : '_property_sheet_item', 'PropertySheet' : '_property_sheet_item',
'Constraint' : '_constraint_item', 'Constraint' : '_constraint_item',
'ModuleComponent' : '_module_component_item',
'Document' : '_document_item', 'Document' : '_document_item',
'Interface': '_interface_item', 'Interface': '_interface_item',
'Mixin': '_mixin_item', 'Mixin': '_mixin_item',
'ToolComponent' : '_tool_component_item',
'Extension' : '_extension_item', 'Extension' : '_extension_item',
'Test' : '_test_item', 'Test' : '_test_item',
'Role' : '_role_item', 'Role' : '_role_item',
...@@ -6095,7 +6115,9 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6095,7 +6115,9 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Text objects (no need to export them into XML) # Text objects (no need to export them into XML)
# XXX Bad naming # XXX Bad naming
item_list_3 = ['_document_item', '_interface_item', '_mixin_item', item_list_3 = ['_module_component_item',
'_document_item', '_interface_item', '_mixin_item',
'_tool_component_item',
'_property_sheet_item', '_property_sheet_item',
'_constraint_item', '_extension_item', '_constraint_item', '_extension_item',
'_test_item', '_message_translation_item',] '_test_item', '_message_translation_item',]
...@@ -6341,13 +6363,48 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6341,13 +6363,48 @@ Business Template is a set of definitions, such as skins, portal types and categ
setattr(self, 'template_portal_type_base_category', ()) setattr(self, 'template_portal_type_base_category', ())
return return
@staticmethod def _getWorkingCopyPathList(self):
def _getAllFilesystemModuleFromPortalTypeIdList(portal_type_id_list): working_copy_list = self.getPortalObject().portal_preferences.getPreferredWorkingCopyList([])
if not working_copy_list:
raise RuntimeError("No 'Working Copies' set in Preferences")
return [ os.path.realpath(p) for p in working_copy_list ]
def _checkFilesystemModulePath(self,
module_obj,
working_copy_path_list):
"""
Return the path of the given module iff its path is in Working Copies
"""
try:
module_path = inspect.getsourcefile(module_obj)
except TypeError:
# No file, builtin or 'erp5' dynamic module
return None
else:
# Canonicalize all paths before comparing (at the same time this will
# take care of trailing '/')
module_realpath = os.path.realpath(module_path)
for working_copy_path in working_copy_path_list:
working_copy_path = os.path.realpath(working_copy_path)
if working_copy_path.endswith('bt5'):
working_copy_path = working_copy_path[:-3]
if module_realpath.startswith(working_copy_path):
# Product
if module_path.endswith('/__init__.py'):
return module_path.rsplit('/', 1)[0]
else:
return module_path
return None
def _getAllFilesystemModuleFromPortalTypeIdList(self, portal_type_id_list):
import erp5.portal_type import erp5.portal_type
import inspect
import Products.ERP5Type import Products.ERP5Type
import zope.interface
product_base_path = inspect.getfile(Products.ERP5Type).rsplit('/', 2)[0] working_copy_path_list = self._getWorkingCopyPathList()
seen_cls_set = set() seen_cls_set = set()
for portal_type in portal_type_id_list: for portal_type in portal_type_id_list:
# According to ObjectTemplateItem.__init__, this could happen (see # According to ObjectTemplateItem.__init__, this could happen (see
...@@ -6366,24 +6423,22 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6366,24 +6423,22 @@ Business Template is a set of definitions, such as skins, portal types and categ
error=True) error=True)
continue continue
for cls in portal_type_cls.mro(): for cls in (tuple(zope.interface.implementedBy(portal_type_cls)) +
if (not cls.__module__.startswith('erp5.') and portal_type_cls.mro()):
cls not in seen_cls_set): if cls in seen_cls_set:
continue
seen_cls_set.add(cls) seen_cls_set.add(cls)
try:
cls_path = inspect.getfile(cls) cls_module_filepath = self._checkFilesystemModulePath(
except TypeError: inspect.getmodule(cls),
pass working_copy_path_list)
else: if cls_module_filepath is not None:
if cls_path.startswith(product_base_path): cls_module_name = cls.__module__
cls_name = cls.__name__ yield cls.__name__, cls.__module__, cls_module_filepath
cls_module = cls.__module__
yield cls_name, cls_module, cls_path
security.declareProtected(Permissions.ManagePortal, security.declareProtected(Permissions.ManagePortal,
'getMigratableSourceCodeFromFilesystemList') 'getMigratableSourceCodeFromFilesystemList')
def getMigratableSourceCodeFromFilesystemList(self, def getMigratableSourceCodeFromFilesystemList(self,
current_bt_only=False,
*args, *args,
**kwargs): **kwargs):
""" """
...@@ -6430,35 +6485,112 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6430,35 +6485,112 @@ Business Template is a set of definitions, such as skins, portal types and categ
# Inspect Portal Types classes mro() of this Business Template to find # Inspect Portal Types classes mro() of this Business Template to find
# Products Documents to migrate by default # Products Documents to migrate by default
portal_type_module_set = set( portal_type_module_filepath_set = set([
filepath for _, _, filepath in
self._getAllFilesystemModuleFromPortalTypeIdList( self._getAllFilesystemModuleFromPortalTypeIdList(
self.getTemplatePortalTypeIdList())) self.getTemplatePortalTypeIdList())])
working_copy_path_list = self._getWorkingCopyPathList()
import Products
for product_name, product_obj in inspect.getmembers(Products,
inspect.ismodule):
if (product_name[0] == '_' or
# Returned by inspect.getmembers()
product_name == 'this_module' or
product_name in (
# Never going to be migrated (bootstrap)
'ERP5Type',
# Probably going to be migrated but at the end and should not be
# done for now (especially ActiveObject and HBTreeFolder2
# classes found in the MRO of most classes)
'CMFActivity', 'HBTreeFolder2')):
continue
# XXX: Only migrate Documents in ERP5 for the moment... product_base_path = self._checkFilesystemModulePath(
import Products.ERP5.Document product_obj,
for name, obj in Products.ERP5.Document.__dict__.iteritems(): working_copy_path_list)
if not name.startswith('_') and inspect.ismodule(obj): if product_base_path is None:
source_reference = obj.__name__ continue
migrate = ((name, source_reference, inspect.getfile(obj)) # 'Module Component': Only handle Product top-level modules
in portal_type_module_set) for submodule_name, submodule_obj in inspect.getmembers(product_obj,
if current_bt_only and not migrate: inspect.ismodule):
if (submodule_name[0] == '_' or
submodule_name in ('this_module', 'Permissions')):
continue continue
obj = __newTempComponent(portal_type='Document Component', try:
reference=name, submodule_filepath = inspect.getsourcefile(submodule_obj)
except TypeError:
# No file, builtin?
continue
if submodule_filepath and submodule_filepath.rsplit('/', 1)[0] == product_base_path:
source_reference = submodule_obj.__name__
migrate = submodule_filepath in portal_type_module_filepath_set
obj = __newTempComponent(portal_type='Module Component',
reference=submodule_name,
source_reference=source_reference,
migrate=migrate)
# {Document,interfaces,mixin,Tool,tests} directories
for directory_name, component_portal_type in (
('Document', 'Document Component'),
('interfaces', 'Interface Component'),
('mixin', 'Mixin Component'),
('Tool', 'Tool Component'),
('tests', 'Test Component')):
submodule_name = '%s.%s' % (product_obj.__name__, directory_name)
for filepath in glob.iglob("%s/%s/*.py" % (product_base_path,
directory_name)):
subsubmodule_name = os.path.splitext(os.path.basename(filepath))[0]
if subsubmodule_name == '__init__':
continue
subsubmodule_portal_type = component_portal_type
source_reference = "%s.%s" % (submodule_name, subsubmodule_name)
migrate = filepath in portal_type_module_filepath_set
if component_portal_type == 'Test Component':
# For non test classes (Mixin, utils...)
if not subsubmodule_name.startswith('test'):
subsubmodule_portal_type = 'Module Component'
# FS: Products/ERP5ShortMessage/interfaces/sms_sending_gateway.py: ISmsSendingGateway
# => ZODB Component Reference: ISmsSendingGateway
elif component_portal_type == 'Interface Component':
# Generally: foo_bar.py => IFooBar, but to avoid quirks (such as
# 'sql_foo.py' => 'ISQLFoo'), get the Interface class __name__
try:
interface_module = __import__(source_reference, {}, {}, source_reference)
except ImportError, e:
LOG("BusinessTemplate", WARNING,
"Skipping %s: Cannot be imported (%s)" % (filepath, e))
continue
from zope.interface.interface import InterfaceClass
interface_class_name = None
for _, m in inspect.getmembers(interface_module):
if (isinstance(m, InterfaceClass) and
# Local definition only
m.__module__ == interface_module.__name__):
if interface_class_name is not None:
# Do not try to be clever, just let the developer fix the problem
LOG("BusinessTemplate", WARNING,
"Skipping %s: More than one InterfaceClass defined" % filepath)
interface_class_name = None
break
else:
interface_class_name = m.__name__
if interface_class_name is None:
continue
subsubmodule_name = interface_class_name
obj = __newTempComponent(portal_type=subsubmodule_portal_type,
reference=subsubmodule_name,
source_reference=source_reference, source_reference=source_reference,
migrate=migrate) migrate=migrate)
if not current_bt_only:
import Products.ERP5.tests
from glob import iglob
for test_path in iglob("%s/test*.py" %
inspect.getfile(Products.ERP5.tests).rsplit('/', 1)[0]):
reference = test_path.rsplit('/', 1)[1][:-3]
obj = __newTempComponent(portal_type='Test Component',
reference=reference,
source_reference="Products.ERP5.tests." + reference)
# Automatically select ZODB Components to be migrated in Migration Dialog # Automatically select ZODB Components to be migrated in Migration Dialog
selection_name = kwargs.get('selection_name') selection_name = kwargs.get('selection_name')
...@@ -6490,13 +6622,18 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6490,13 +6622,18 @@ Business Template is a set of definitions, such as skins, portal types and categ
list_selection_name = kw.get('list_selection_name') list_selection_name = kw.get('list_selection_name')
migrated_product_module_set = set() migrated_product_module_set = set()
template_module_component_id_set = set(self.getTemplateModuleComponentIdList())
template_mixin_id_set = set(self.getTemplateMixinIdList())
template_interface_id_set = set(self.getTemplateInterfaceIdList())
template_document_id_set = set(self.getTemplateDocumentIdList()) template_document_id_set = set(self.getTemplateDocumentIdList())
template_tool_component_id_set = set(self.getTemplateToolComponentIdList())
template_extension_id_set = set(self.getTemplateExtensionIdList()) template_extension_id_set = set(self.getTemplateExtensionIdList())
template_test_id_set = set(self.getTemplateTestIdList()) template_test_id_set = set(self.getTemplateTestIdList())
if list_selection_name is None: if list_selection_name is None:
temp_obj_list = self.getMigratableSourceCodeFromFilesystemList( # Programmatically called, not through the UI
current_bt_only=True) temp_obj_list = [ temp_obj for temp_obj in self.getMigratableSourceCodeFromFilesystemList()
if temp_obj.migrate ]
else: else:
from base64 import b64decode from base64 import b64decode
import cPickle import cPickle
...@@ -6521,13 +6658,22 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6521,13 +6658,22 @@ Business Template is a set of definitions, such as skins, portal types and categ
return return
filesystem_zodb_module_mapping_set = set()
for temp_obj in temp_obj_list:
source_reference = temp_obj.getSourceReference()
if source_reference is not None:
filesystem_zodb_module_mapping_set.add(
(source_reference, "%s.%s" % (temp_obj._getDynamicModuleNamespace(),
temp_obj.getReference())))
for temp_obj in temp_obj_list: for temp_obj in temp_obj_list:
source_reference = temp_obj.getSourceReference() source_reference = temp_obj.getSourceReference()
try: try:
obj = temp_obj.importFromFilesystem(component_tool, obj = temp_obj.importFromFilesystem(component_tool,
temp_obj.getReference(), temp_obj.getReference(),
version, version,
source_reference) source_reference,
filesystem_zodb_module_mapping_set)
except Exception, e: except Exception, e:
LOG("BusinessTemplate", WARNING, LOG("BusinessTemplate", WARNING,
"Could not import component '%s' ('%s') from the filesystem" % "Could not import component '%s' ('%s') from the filesystem" %
...@@ -6542,6 +6688,14 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6542,6 +6688,14 @@ Business Template is a set of definitions, such as skins, portal types and categ
id_set = template_extension_id_set id_set = template_extension_id_set
elif portal_type == 'Test Component': elif portal_type == 'Test Component':
id_set = template_test_id_set id_set = template_test_id_set
elif portal_type == 'Mixin Component':
id_set = template_mixin_id_set
elif portal_type == 'Module Component':
id_set = template_module_component_id_set
elif portal_type == 'Interface Component':
id_set = template_interface_id_set
elif portal_type == 'Tool Component':
id_set = template_tool_component_id_set
# 'Document Component' # 'Document Component'
else: else:
id_set = template_document_id_set id_set = template_document_id_set
...@@ -6567,12 +6721,19 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -6567,12 +6721,19 @@ Business Template is a set of definitions, such as skins, portal types and categ
transaction.abort() transaction.abort()
raise RuntimeError(message) raise RuntimeError(message)
self.setTemplateModuleComponentIdList(sorted(template_module_component_id_set))
self.setTemplateMixinIdList(sorted(template_mixin_id_set))
self.setTemplateInterfaceIdList(sorted(template_interface_id_set))
self.setTemplateDocumentIdList(sorted(template_document_id_set)) self.setTemplateDocumentIdList(sorted(template_document_id_set))
self.setTemplateToolComponentIdList(sorted(template_tool_component_id_set))
self.setTemplateExtensionIdList(sorted(template_extension_id_set)) self.setTemplateExtensionIdList(sorted(template_extension_id_set))
self.setTemplateTestIdList(sorted(template_test_id_set)) self.setTemplateTestIdList(sorted(template_test_id_set))
# This will trigger a reset so that Portal Types mro() can be checked # This will trigger a reset so that Portal Types mro() can be checked
# after migration for filesystem Products modules still being used # after migration for filesystem Products modules still being used
#
# TODO-arnau: checkPythonSource code done twice (importFromFilesystem()
# and newContent() through Interaction Workflow)
transaction.commit() transaction.commit()
still_used_list_dict = {} still_used_list_dict = {}
......
...@@ -69,9 +69,11 @@ item_name_list = ( ...@@ -69,9 +69,11 @@ item_name_list = (
'registered_version_priority_selection', 'registered_version_priority_selection',
'workflow', 'workflow',
'product', 'product',
'module_component',
'document', 'document',
'interface', 'interface',
'mixin', 'mixin',
'tool_component',
'property_sheet', 'property_sheet',
'constraint', 'constraint',
'extension', 'extension',
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ComponentMixin_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: object is not None and not object.isWebMode()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ComponentMixin_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: object is not None and not object.isWebMode()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -53,7 +53,9 @@ ...@@ -53,7 +53,9 @@
<item>Extension Component</item> <item>Extension Component</item>
<item>Interface Component</item> <item>Interface Component</item>
<item>Mixin Component</item> <item>Mixin Component</item>
<item>Module Component</item>
<item>Test Component</item> <item>Test Component</item>
<item>Tool Component</item>
</portal_type> </portal_type>
<portal_type id="Contribution Registry Tool"> <portal_type id="Contribution Registry Tool">
<item>Contribution Predicate</item> <item>Contribution Predicate</item>
......
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
<portal_type id="Mixin Component"> <portal_type id="Mixin Component">
<item>SortIndex</item> <item>SortIndex</item>
</portal_type> </portal_type>
<portal_type id="Module Component">
<item>SortIndex</item>
</portal_type>
<portal_type id="Property Existence Constraint"> <portal_type id="Property Existence Constraint">
<item>ConstraintType</item> <item>ConstraintType</item>
</portal_type> </portal_type>
...@@ -79,6 +82,9 @@ ...@@ -79,6 +82,9 @@
<portal_type id="Test Component"> <portal_type id="Test Component">
<item>SortIndex</item> <item>SortIndex</item>
</portal_type> </portal_type>
<portal_type id="Tool Component">
<item>SortIndex</item>
</portal_type>
<portal_type id="Trash Bin"> <portal_type id="Trash Bin">
<item>Base</item> <item>Base</item>
<item>SimpleItem</item> <item>SimpleItem</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>web_page.png</string> </value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Text Document</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>A Module Component is any non-persistent Module containing classes definitions (used to be found at the root of Products).</string> </value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addDocumentComponent</string> </value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Module Component</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>ModuleComponent</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_content</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_content</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>web_page.png</string> </value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Text Document</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>A Tool Component as used to be found in Products.XXX.Tool</string> </value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addDocumentComponent</string> </value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Tool Component</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>ToolComponent</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_content</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_content</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -123,6 +123,10 @@ ...@@ -123,6 +123,10 @@
<type>Mixin Component</type> <type>Mixin Component</type>
<workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow> <workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow>
</chain> </chain>
<chain>
<type>Module Component</type>
<workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow>
</chain>
<chain> <chain>
<type>Predicate</type> <type>Predicate</type>
<workflow>edit_workflow</workflow> <workflow>edit_workflow</workflow>
...@@ -175,4 +179,8 @@ ...@@ -175,4 +179,8 @@
<type>Test Component</type> <type>Test Component</type>
<workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow> <workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow>
</chain> </chain>
<chain>
<type>Tool Component</type>
<workflow>component_validation_workflow, dynamic_class_generation_interaction_workflow, edit_workflow</workflow>
</chain>
</workflow_chain> </workflow_chain>
\ No newline at end of file
...@@ -64,11 +64,13 @@ ...@@ -64,11 +64,13 @@
<list> <list>
<string>my_template_role_list</string> <string>my_template_role_list</string>
<string>my_template_site_property_id_list</string> <string>my_template_site_property_id_list</string>
<string>my_template_module_component_id_list</string>
<string>my_template_interface_id_list</string> <string>my_template_interface_id_list</string>
<string>my_template_mixin_id_list</string> <string>my_template_mixin_id_list</string>
<string>my_template_document_id_list</string> <string>my_template_document_id_list</string>
<string>my_template_property_sheet_id_list</string> <string>my_template_property_sheet_id_list</string>
<string>my_template_extension_id_list</string> <string>my_template_extension_id_list</string>
<string>my_template_tool_component_id_list</string>
<string>my_template_tool_id_list</string> <string>my_template_tool_id_list</string>
<string>my_template_preference_list</string> <string>my_template_preference_list</string>
<string>my_template_update_tool</string> <string>my_template_update_tool</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="LinesField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_template_module_component_id_list</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>line_too_long</string> </key>
<value> <string>A line was too long.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>You entered too many characters.</string> </value>
</item>
<item>
<key> <string>too_many_lines</string> </key>
<value> <string>You entered too many lines.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>5</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Module Components</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string> </string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>40</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="LinesField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_template_tool_component_id_list</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
<item>
<key> <string>line_too_long</string> </key>
<value> <string>A line was too long.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>You entered too many characters.</string> </value>
</item>
<item>
<key> <string>too_many_lines</string> </key>
<value> <string>You entered too many lines.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>5</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Tool Components</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>view_separator</string> </key>
<value> <string> </string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>40</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -2,31 +2,27 @@ ...@@ -2,31 +2,27 @@
<ZopeData> <ZopeData>
<record id="1" aka="AAAAAAAAAAE="> <record id="1" aka="AAAAAAAAAAE=">
<pickle> <pickle>
<global name="Document Component" module="erp5.portal_type"/> <global name="Tool Component" module="erp5.portal_type"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>CallableTool</string> </value> <value> <string>CallableTool</string> </value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <string>A tool that can be used to add scripts and other callable methods (including ZSQL templates and HTML templates) to ERP5. It replaces portal_skins</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>document.erp5.CallableTool</string> </value> <value> <string>tool.erp5.CallableTool</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value> <value> <string>Tool Component</string> </value>
</item> </item>
<item> <item>
<key> <string>sid</string> </key> <key> <string>sid</string> </key>
...@@ -53,28 +49,13 @@ ...@@ -53,28 +49,13 @@
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="PersistentMapping" module="Persistence.mapping"/> <global name="PersistentMapping" module="Persistence.mapping"/>
</pickle> </pickle>
...@@ -87,7 +68,7 @@ ...@@ -87,7 +68,7 @@
<item> <item>
<key> <string>component_validation_workflow</string> </key> <key> <string>component_validation_workflow</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -96,7 +77,7 @@ ...@@ -96,7 +77,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/> <global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle> </pickle>
......
...@@ -2,31 +2,27 @@ ...@@ -2,31 +2,27 @@
<ZopeData> <ZopeData>
<record id="1" aka="AAAAAAAAAAE="> <record id="1" aka="AAAAAAAAAAE=">
<pickle> <pickle>
<global name="Document Component" module="erp5.portal_type"/> <global name="Tool Component" module="erp5.portal_type"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
<value> <string>DiffTool</string> </value> <value> <string>DiffTool</string> </value>
</item> </item>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
<value> <string>A tool than can be used to Diff any 2 objects of same module.</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>document.erp5.DiffTool</string> </value> <value> <string>tool.erp5.DiffTool</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value> <value> <string>Tool Component</string> </value>
</item> </item>
<item> <item>
<key> <string>sid</string> </key> <key> <string>sid</string> </key>
...@@ -53,28 +49,13 @@ ...@@ -53,28 +49,13 @@
<item> <item>
<key> <string>workflow_history</string> </key> <key> <string>workflow_history</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="PersistentMapping" module="Persistence.mapping"/> <global name="PersistentMapping" module="Persistence.mapping"/>
</pickle> </pickle>
...@@ -87,7 +68,7 @@ ...@@ -87,7 +68,7 @@
<item> <item>
<key> <string>component_validation_workflow</string> </key> <key> <string>component_validation_workflow</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
</dictionary> </dictionary>
...@@ -96,7 +77,7 @@ ...@@ -96,7 +77,7 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/> <global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle> </pickle>
......
...@@ -74,7 +74,9 @@ ...@@ -74,7 +74,9 @@
<string>Extension Component</string> <string>Extension Component</string>
<string>Interface Component</string> <string>Interface Component</string>
<string>Mixin Component</string> <string>Mixin Component</string>
<string>Module Component</string>
<string>Test Component</string> <string>Test Component</string>
<string>Tool Component</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -73,7 +73,9 @@ ...@@ -73,7 +73,9 @@
<string>Extension Component</string> <string>Extension Component</string>
<string>Interface Component</string> <string>Interface Component</string>
<string>Mixin Component</string> <string>Mixin Component</string>
<string>Module Component</string>
<string>Test Component</string> <string>Test Component</string>
<string>Tool Component</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -89,6 +89,7 @@ Interface Component | view ...@@ -89,6 +89,7 @@ Interface Component | view
Memcached Plugin | view Memcached Plugin | view
Memcached Tool | view Memcached Tool | view
Mixin Component | view Mixin Component | view
Module Component | view
Predicate | view Predicate | view
Preference Tool Type | jump_property_sheets Preference Tool Type | jump_property_sheets
Preference Tool Type | view Preference Tool Type | view
...@@ -147,6 +148,7 @@ Template Tool | upgrade_from_repository ...@@ -147,6 +148,7 @@ Template Tool | upgrade_from_repository
Template Tool | view Template Tool | view
Test Component | run_live_test Test Component | run_live_test
Test Component | view Test Component | view
Tool Component | view
Trash Bin | jump_bt5 Trash Bin | jump_bt5
Trash Bin | view Trash Bin | view
Trash Folder | view Trash Folder | view
......
document.erp5.DiffTool
document.erp5.ScriptConstraint document.erp5.ScriptConstraint
document.erp5.TransformOdtToDocx document.erp5.TransformOdtToDocx
document.erp5.TransformOdtToHtml document.erp5.TransformOdtToHtml
...@@ -28,4 +27,3 @@ document.erp5.TransformPptxToPpty ...@@ -28,4 +27,3 @@ document.erp5.TransformPptxToPpty
document.erp5.TransformPptyToPptx document.erp5.TransformPptyToPptx
document.erp5.TransformImageToPcx document.erp5.TransformImageToPcx
document.erp5.TransformImageToBmp document.erp5.TransformImageToBmp
\ No newline at end of file
document.erp5.CallableTool
\ No newline at end of file
...@@ -23,7 +23,9 @@ Component Tool | Document Component ...@@ -23,7 +23,9 @@ Component Tool | Document Component
Component Tool | Extension Component Component Tool | Extension Component
Component Tool | Interface Component Component Tool | Interface Component
Component Tool | Mixin Component Component Tool | Mixin Component
Component Tool | Module Component
Component Tool | Test Component Component Tool | Test Component
Component Tool | Tool Component
Contribution Registry Tool | Contribution Predicate Contribution Registry Tool | Contribution Predicate
Domain Tool | Base Domain Domain Tool | Base Domain
Domain | Domain Domain | Domain
......
...@@ -53,6 +53,7 @@ Mapped Value ...@@ -53,6 +53,7 @@ Mapped Value
Memcached Plugin Memcached Plugin
Memcached Tool Memcached Tool
Mixin Component Mixin Component
Module Component
Movement Movement
Notification Tool Notification Tool
Order Tool Order Tool
...@@ -85,6 +86,7 @@ TALES Constraint ...@@ -85,6 +86,7 @@ TALES Constraint
Template Tool Template Tool
Test Component Test Component
Test Tool Test Tool
Tool Component
Trash Bin Trash Bin
Trash Folder Trash Folder
Trash Tool Trash Tool
......
...@@ -14,6 +14,7 @@ Document Component | SortIndex ...@@ -14,6 +14,7 @@ Document Component | SortIndex
Extension Component | SortIndex Extension Component | SortIndex
Interface Component | SortIndex Interface Component | SortIndex
Mixin Component | SortIndex Mixin Component | SortIndex
Module Component | SortIndex
Property Existence Constraint | ConstraintType Property Existence Constraint | ConstraintType
Property Type Validity Constraint | ConstraintType Property Type Validity Constraint | ConstraintType
Python Script | CatalogFilter Python Script | CatalogFilter
...@@ -29,6 +30,7 @@ Simulation Movement | SortIndex ...@@ -29,6 +30,7 @@ Simulation Movement | SortIndex
String Attribute Match Constraint | ConstraintType String Attribute Match Constraint | ConstraintType
TALES Constraint | ConstraintType TALES Constraint | ConstraintType
Test Component | SortIndex Test Component | SortIndex
Tool Component | SortIndex
Trash Bin | Base Trash Bin | Base
Trash Bin | SimpleItem Trash Bin | SimpleItem
Trash Bin | Task Trash Bin | Task
\ No newline at end of file
...@@ -40,6 +40,9 @@ Memcached Plugin | memcached_plugin_interaction_workflow ...@@ -40,6 +40,9 @@ Memcached Plugin | memcached_plugin_interaction_workflow
Mixin Component | component_validation_workflow Mixin Component | component_validation_workflow
Mixin Component | dynamic_class_generation_interaction_workflow Mixin Component | dynamic_class_generation_interaction_workflow
Mixin Component | edit_workflow Mixin Component | edit_workflow
Module Component | component_validation_workflow
Module Component | dynamic_class_generation_interaction_workflow
Module Component | edit_workflow
Predicate | edit_workflow Predicate | edit_workflow
Preference | edit_workflow Preference | edit_workflow
Preference | preference_workflow Preference | preference_workflow
...@@ -57,3 +60,6 @@ TALES Constraint | dynamic_class_generation_interaction_workflow ...@@ -57,3 +60,6 @@ TALES Constraint | dynamic_class_generation_interaction_workflow
Test Component | component_validation_workflow Test Component | component_validation_workflow
Test Component | dynamic_class_generation_interaction_workflow Test Component | dynamic_class_generation_interaction_workflow
Test Component | edit_workflow Test Component | edit_workflow
Tool Component | component_validation_workflow
Tool Component | dynamic_class_generation_interaction_workflow
Tool Component | edit_workflow
\ No newline at end of file
tool.erp5.CallableTool
tool.erp5.DiffTool
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/lines</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>A list of IDs of Module (not a Tool, Document, Interface or Mixin) ZODB Components</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_module_component_id_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: ()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/lines</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>A list of IDs of Tool ZODB Components</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>template_tool_component_id_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: ()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -7840,6 +7840,7 @@ class _ProductMigrationTemplateItemMixin: ...@@ -7840,6 +7840,7 @@ class _ProductMigrationTemplateItemMixin:
""" """
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_string = """ sequence_string = """
SetPreferredWorkingCopyList
CreateProductDocumentAndPortalType CreateProductDocumentAndPortalType
CreateNewBusinessTemplate CreateNewBusinessTemplate
UseExportBusinessTemplate UseExportBusinessTemplate
...@@ -8189,6 +8190,30 @@ class _LocalTemplateItemMixin: ...@@ -8189,6 +8190,30 @@ class _LocalTemplateItemMixin:
sequence_list.addSequenceString(sequence_string) sequence_list.addSequenceString(sequence_string)
sequence_list.play(self) sequence_list.play(self)
def stepSetPreferredWorkingCopyList(self, sequence=None, **kw):
"""
TODO: Merge with Zuite_setPreference (ERP5TypeFunctionalTestCase)?
"""
pref = getattr(self.portal.portal_preferences,
"testBusinessTemplate_test_preference",
None)
if pref is None:
pref = self.portal.portal_preferences.newContent(
id="testBusinessTemplate_test_preference",
portal_type="Preference",
priority=1)
import inspect
import Products.ERP5
erp5_product_directory = os.path.realpath(
inspect.getsourcefile(Products.ERP5)).rsplit('/', 1)[0]
pref.setPreferredWorkingCopyList(
[erp5_product_directory.rsplit('/', 2)[0] + '/bt5',
erp5_product_directory + '/bootstrap'])
if pref.getPreferenceState() == 'disabled':
pref.enable()
def stepCopyAndMigrateDocumentBusinessTemplate(self, sequence=None, **kw): def stepCopyAndMigrateDocumentBusinessTemplate(self, sequence=None, **kw):
""" """
Simulate migration from filesystem to ZODB Simulate migration from filesystem to ZODB
...@@ -8243,6 +8268,7 @@ class _LocalTemplateItemMixin: ...@@ -8243,6 +8268,7 @@ class _LocalTemplateItemMixin:
""" """
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_string = '\ sequence_string = '\
SetPreferredWorkingCopyList \
CreateDocument \ CreateDocument \
CreateNewBusinessTemplate \ CreateNewBusinessTemplate \
UseExportBusinessTemplate \ UseExportBusinessTemplate \
...@@ -8313,6 +8339,7 @@ class _LocalTemplateItemMixin: ...@@ -8313,6 +8339,7 @@ class _LocalTemplateItemMixin:
def test_BusinessTemplateUpgradeDocumentFromFilesystemToZodb(self): def test_BusinessTemplateUpgradeDocumentFromFilesystemToZodb(self):
sequence_list = SequenceList() sequence_list = SequenceList()
sequence_string = """ sequence_string = """
SetPreferredWorkingCopyList
CreateDocument CreateDocument
CreateNewBusinessTemplate CreateNewBusinessTemplate
UseExportBusinessTemplate UseExportBusinessTemplate
......
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
# #
############################################################################## ##############################################################################
from Products.ERP5Type.mixin.component import ComponentMixin from Products.ERP5Type.Core.ModuleComponent import ModuleComponent
from Products.ERP5Type.mixin.text_content_history import TextContentHistoryMixin
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
...@@ -37,7 +37,7 @@ import zope.interface ...@@ -37,7 +37,7 @@ import zope.interface
import re import re
from Products.ERP5Type.interfaces.component import IComponent from Products.ERP5Type.interfaces.component import IComponent
class DocumentComponent(ComponentMixin, TextContentHistoryMixin): class DocumentComponent(ModuleComponent):
""" """
ZODB Component for Documents in bt5 only for now (which used to be installed ZODB Component for Documents in bt5 only for now (which used to be installed
in INSTANCE_HOME/Document) but this will also be used later on for Documents in INSTANCE_HOME/Document) but this will also be used later on for Documents
......
...@@ -27,15 +27,15 @@ ...@@ -27,15 +27,15 @@
# #
############################################################################## ##############################################################################
from Products.ERP5Type.mixin.component import ComponentMixin from Products.ERP5Type.Core.ModuleComponent import ModuleComponent
from Products.ERP5Type.mixin.text_content_history import TextContentHistoryMixin
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
import zope.interface import zope.interface
from Products.ERP5Type.interfaces.component import IComponent from Products.ERP5Type.interfaces.component import IComponent
class ExtensionComponent(ComponentMixin, TextContentHistoryMixin): class ExtensionComponent(ModuleComponent):
""" """
ZODB Component for Extensions previously defined in the bt5 and installed in ZODB Component for Extensions previously defined in the bt5 and installed in
INSTANCE_HOME/Extensions INSTANCE_HOME/Extensions
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
# Arnaud Fontaine <arnaud.fontaine@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from Products.ERP5Type.mixin.component import ComponentMixin
from Products.ERP5Type.mixin.text_content_history import TextContentHistoryMixin
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
import zope.interface
from Products.ERP5Type.interfaces.component import IComponent
class ModuleComponent(ComponentMixin, TextContentHistoryMixin):
"""
ZODB Component for Modules, eg non-Documents from Products, and the base
class for all other Components
"""
meta_type = 'ERP5 Module Component'
portal_type = 'Module Component'
zope.interface.implements(IComponent)
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
@staticmethod
def _getFilesystemPath():
# TODO-arnau: useful?
raise NotImplementedError
@staticmethod
def _getDynamicModuleNamespace():
return 'erp5.component.module'
@staticmethod
def getIdPrefix():
return 'module'
...@@ -27,15 +27,15 @@ ...@@ -27,15 +27,15 @@
# #
############################################################################## ##############################################################################
from Products.ERP5Type.mixin.component import ComponentMixin from Products.ERP5Type.Core.ModuleComponent import ModuleComponent
from Products.ERP5Type.mixin.text_content_history import TextContentHistoryMixin
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
import zope.interface import zope.interface
from Products.ERP5Type.interfaces.component import IComponent from Products.ERP5Type.interfaces.component import IComponent
class TestComponent(ComponentMixin, TextContentHistoryMixin): class TestComponent(ModuleComponent):
""" """
ZODB Component for Live Tests only (previously defined in the bt5 and ZODB Component for Live Tests only (previously defined in the bt5 and
installed in INSTANCE_HOME/tests) as other kind of Tests should be installed in INSTANCE_HOME/tests) as other kind of Tests should be
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2019 Nexedi SA and Contributors. All Rights Reserved.
# Arnaud Fontaine <arnaud.fontaine@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
from Products.ERP5Type.Core.DocumentComponent import DocumentComponent
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
import zope.interface
from Products.ERP5Type.interfaces.component import IComponent
from Products.CMFCore import utils
class ToolComponent(DocumentComponent):
"""
ZODB Component for Tools, used to be found on Products.XXX.Tool on FS
"""
meta_type = 'ERP5 Tool Component'
portal_type = 'Tool Component'
zope.interface.implements(IComponent)
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def _hookAfterLoad(self, module_obj):
"""
Register Tool so that it can be added in ZMI through manage_addToolForm.
For FS Tools, this is done during Product initialize() by
Products.CMFCore.utils.ToolInit.
"""
tool_class = getattr(module_obj, self.getReference())
# Should we really use ERP5 Product and not ERP5Type considering that ERP5
# may be gone at some point? Or the other way around? For now, all tools
# have meta_type='ERP5 ...' so just use ERP5 Product.
import Products.ERP5
toolinit = Products.ERP5.__FactoryDispatcher__.toolinit
# Products.CMFCore.utils.ToolInit.initialize()
tool_class.__factory_meta_type__ = toolinit.meta_type
tool_class.icon = 'misc_/%s/%s' % ('ERP5', toolinit.icon)
toolinit.tools.add(tool_class)
@staticmethod
def _getFilesystemPath():
# TODO-arnau: useful?
raise NotImplementedError
@staticmethod
def _getDynamicModuleNamespace():
return 'erp5.component.tool'
@staticmethod
def getIdPrefix():
return 'tool'
...@@ -61,7 +61,9 @@ class PropertySheetTool(BaseTool): ...@@ -61,7 +61,9 @@ class PropertySheetTool(BaseTool):
_bootstrap_business_template_property_tuple = ( _bootstrap_business_template_property_tuple = (
'template_catalog_security_uid_column_property', 'template_catalog_security_uid_column_property',
'template_interface_id_property', 'template_interface_id_property',
'template_mixin_id_property') 'template_mixin_id_property',
'template_module_component_id_property',
'template_tool_component_id_property')
def _isBootstrapRequired(self): def _isBootstrapRequired(self):
if not self.has_key('BaseType'): if not self.has_key('BaseType'):
......
...@@ -257,7 +257,8 @@ class TypesTool(TypeProvider): ...@@ -257,7 +257,8 @@ class TypesTool(TypeProvider):
Documents) that can be used as Base classes Documents) that can be used as Base classes
""" """
from Products.ERP5Type import document_class_registry from Products.ERP5Type import document_class_registry
return self._getTypeList('Document Component', document_class_registry) return self._getTypeList(('Document Component', 'Tool Component'),
document_class_registry)
security.declareProtected(Permissions.AccessContentsInformation, 'getMixinTypeList') security.declareProtected(Permissions.AccessContentsInformation, 'getMixinTypeList')
def getMixinTypeList(self): def getMixinTypeList(self):
......
...@@ -1168,13 +1168,13 @@ def initializeProduct( context, ...@@ -1168,13 +1168,13 @@ def initializeProduct( context,
registerDocumentClass(tool.__module__, tool.__name__) registerDocumentClass(tool.__module__, tool.__name__)
try: try:
utils.ToolInit('%s Tool' % product_name, utils.ToolInit('%s Tool' % product_name,
tools=tools, tools=set(tools),
icon='tool.png', icon='tool.png',
).initialize( context ) ).initialize( context )
except TypeError: except TypeError:
# product_name parameter is deprecated in CMF # product_name parameter is deprecated in CMF
utils.ToolInit('%s Tool' % product_name, utils.ToolInit('%s Tool' % product_name,
tools=tools, tools=set(tools),
product_name=product_name, product_name=product_name,
icon='tool.png', icon='tool.png',
).initialize( context ) ).initialize( context )
......
...@@ -318,10 +318,12 @@ class ComponentDynamicPackage(ModuleType): ...@@ -318,10 +318,12 @@ class ComponentDynamicPackage(ModuleType):
import erp5.component import erp5.component
erp5.component.ref_manager.add_module(module) erp5.component.ref_manager.add_module(module)
return module
finally: finally:
imp.release_lock() imp.release_lock()
component._hookAfterLoad(module)
return module
def load_module(self, fullname): def load_module(self, fullname):
""" """
Make sure that loading module is thread-safe using aq_method_lock to make Make sure that loading module is thread-safe using aq_method_lock to make
...@@ -411,3 +413,18 @@ class ComponentDynamicPackage(ModuleType): ...@@ -411,3 +413,18 @@ class ComponentDynamicPackage(ModuleType):
del sys.modules[module_name] del sys.modules[module_name]
delattr(package, name) delattr(package, name)
class ToolComponentDynamicPackage(ComponentDynamicPackage):
def reset(self, *args, **kw):
"""
Reset CMFCore list of Tools (manage_addToolForm)
"""
import Products.ERP5
toolinit = Products.ERP5.__FactoryDispatcher__.toolinit
reset_tool_set = set()
for tool in toolinit.tools:
if not tool.__module__.startswith(self._namespace_prefix):
reset_tool_set.add(tool)
toolinit.tools = reset_tool_set
super(ToolComponentDynamicPackage, self).reset(*args, **kw)
...@@ -180,12 +180,16 @@ def initializeDynamicModules(): ...@@ -180,12 +180,16 @@ def initializeDynamicModules():
holds accessors holders of Portal Types holds accessors holders of Portal Types
erp5.component: erp5.component:
holds ZODB Component packages holds ZODB Component packages
erp5.component.module:
holds Module (eg any module not being one of the following and used to be found in Products.NAME on FS)
erp5.component.document: erp5.component.document:
holds Document modules previously found in bt5 in $INSTANCE_HOME/Document holds Document modules previously found in bt5 in $INSTANCE_HOME/Document
erp5.component.interface: erp5.component.interface:
holds Interface modules previously found in Products.NAME.interfaces holds Interface modules previously found in Products.NAME.interfaces
erp5.component.mixin: erp5.component.mixin:
holds Mixin modules previously found in Products.NAME.mixin holds Mixin modules previously found in Products.NAME.mixin
erp5.component.Tool:
holds Tool modules previously found in Products.NAME.Tool
erp5.component.extension: erp5.component.extension:
holds Extension modules previously found in bt5 in holds Extension modules previously found in bt5 in
$INSTANCE_HOME/Extensions $INSTANCE_HOME/Extensions
...@@ -221,7 +225,8 @@ def initializeDynamicModules(): ...@@ -221,7 +225,8 @@ def initializeDynamicModules():
# ZODB Components # ZODB Components
erp5.component = ComponentPackageType("erp5.component") erp5.component = ComponentPackageType("erp5.component")
from component_package import ComponentDynamicPackage from component_package import (ComponentDynamicPackage,
ToolComponentDynamicPackage)
# Prevent other threads to create erp5.* packages and modules or seeing them # Prevent other threads to create erp5.* packages and modules or seeing them
# incompletely # incompletely
...@@ -234,12 +239,18 @@ def initializeDynamicModules(): ...@@ -234,12 +239,18 @@ def initializeDynamicModules():
erp5.accessor_holder.property_sheet erp5.accessor_holder.property_sheet
sys.modules["erp5.component"] = erp5.component sys.modules["erp5.component"] = erp5.component
erp5.component.module = ComponentDynamicPackage('erp5.component.module',
'Module Component')
erp5.component.extension = ComponentDynamicPackage('erp5.component.extension', erp5.component.extension = ComponentDynamicPackage('erp5.component.extension',
'Extension Component') 'Extension Component')
erp5.component.document = ComponentDynamicPackage('erp5.component.document', erp5.component.document = ComponentDynamicPackage('erp5.component.document',
'Document Component') 'Document Component')
erp5.component.tool = ToolComponentDynamicPackage('erp5.component.tool',
'Tool Component')
erp5.component.interface = ComponentDynamicPackage('erp5.component.interface', erp5.component.interface = ComponentDynamicPackage('erp5.component.interface',
'Interface Component') 'Interface Component')
......
...@@ -212,6 +212,14 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -212,6 +212,14 @@ def generatePortalTypeClass(site, portal_type_name):
type_class_namespace = document_class_registry.get(type_class, '') type_class_namespace = document_class_registry.get(type_class, '')
if not (type_class_namespace.startswith('Products.ERP5Type') or if not (type_class_namespace.startswith('Products.ERP5Type') or
portal_type_name in core_portal_type_class_dict): portal_type_name in core_portal_type_class_dict):
module = None
if portal_type_name.endswith('Tool'):
import erp5.component.tool
module = erp5.component.tool.find_load_module(type_class)
# Tool Component was introduced recently and some Tool have already been
# migrated as Document Component
if module is None:
import erp5.component.document import erp5.component.document
module = erp5.component.document.find_load_module(type_class) module = erp5.component.document.find_load_module(type_class)
if module is not None: if module is not None:
...@@ -230,7 +238,14 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -230,7 +238,14 @@ def generatePortalTypeClass(site, portal_type_name):
% (type_class, portal_type_name)) % (type_class, portal_type_name))
if klass is None: if klass is None:
try:
klass = _importClass(type_class_path) klass = _importClass(type_class_path)
except ImportError:
error_msg = 'Could not import %s of Portal Type %s' % (type_class,
portal_type_name)
LOG("ERP5Type.Dynamic", WARNING, error_msg, error=True)
raise AttributeError(error_msg)
global property_sheet_generating_portal_type_set global property_sheet_generating_portal_type_set
......
...@@ -36,6 +36,12 @@ class IComponent(Interface): ...@@ -36,6 +36,12 @@ class IComponent(Interface):
Extensions or Documents, or any interfaces, mixin and Documents from Extensions or Documents, or any interfaces, mixin and Documents from
Products. Any Component class must implement this interface Products. Any Component class must implement this interface
""" """
def _hookAfterLoad(self, module_obj):
"""
Idempotent hook called after loading the module
"""
pass
def checkConsistency(obj, *args, **kwargs): def checkConsistency(obj, *args, **kwargs):
""" """
Check the consistency of a ZODB Component when validating from draft state Check the consistency of a ZODB Component when validating from draft state
...@@ -68,7 +74,12 @@ class IComponent(Interface): ...@@ -68,7 +74,12 @@ class IComponent(Interface):
Return the ID prefix for Component objects Return the ID prefix for Component objects
""" """
def importFromFilesystem(cls, context, reference, version, source_reference=None): def importFromFilesystem(cls,
context,
reference,
version,
source_reference=None,
filesystem_zodb_module_mapping_set=None):
""" """
Import a Component from the filesystem into ZODB after checking that the Import a Component from the filesystem into ZODB after checking that the
source code is valid source code is valid
......
...@@ -193,6 +193,9 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -193,6 +193,9 @@ class ComponentMixin(PropertyRecordableMixin, Base):
_message_text_content_not_set = "No source code" _message_text_content_not_set = "No source code"
_message_text_content_error = "Error in Source Code: ${error_message}" _message_text_content_error = "Error in Source Code: ${error_message}"
def _hookAfterLoad(self, module_obj):
pass
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getValidationState') 'getValidationState')
def getValidationState(self): def getValidationState(self):
...@@ -352,7 +355,12 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -352,7 +355,12 @@ class ComponentMixin(PropertyRecordableMixin, Base):
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'importFromFilesystem') 'importFromFilesystem')
@classmethod @classmethod
def importFromFilesystem(cls, context, reference, version, source_reference=None): def importFromFilesystem(cls,
context,
reference,
version,
source_reference=None,
filesystem_zodb_module_mapping_set=None):
""" """
Import a Component from the filesystem into ZODB and validate it so it can Import a Component from the filesystem into ZODB and validate it so it can
be loaded straightaway provided validate() does not raise any error of be loaded straightaway provided validate() does not raise any error of
...@@ -368,7 +376,20 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -368,7 +376,20 @@ class ComponentMixin(PropertyRecordableMixin, Base):
path = inspect.getsourcefile(module_obj) path = inspect.getsourcefile(module_obj)
with open(path) as f: with open(path) as f:
if filesystem_zodb_module_mapping_set is None:
source_code = f.read() source_code = f.read()
else:
source_code_line_list = []
for line in f:
for (filesystem_module,
zodb_module) in filesystem_zodb_module_mapping_set:
if line.startswith("from " + filesystem_module):
line = line.replace(filesystem_module, zodb_module, 1)
break
source_code_line_list.append(line)
source_code = ''.join(source_code_line_list)
# Checking that the source code is syntactically correct is not # Checking that the source code is syntactically correct is not
# needed when importing from filesystem, moreover errors may occur # needed when importing from filesystem, moreover errors may occur
......
...@@ -65,3 +65,37 @@ def _setCacheHeaders(obj, extra_context): ...@@ -65,3 +65,37 @@ def _setCacheHeaders(obj, extra_context):
import Products.CMFCore.utils import Products.CMFCore.utils
Products.CMFCore.utils._setCacheHeaders = _setCacheHeaders Products.CMFCore.utils._setCacheHeaders = _setCacheHeaders
# To load all erp5.component.tool.* so that they can be added through 'ERP5
# Site' => Add 'ERP5 Tool'.
from Products.CMFCore.utils import addInstanceForm
def manage_addToolForm(self, REQUEST):
""" Show the add tool form.
"""
from Products.ERP5.ERP5Site import getSite
import erp5.component.tool
seen_tool_component_set = set()
for tool_component in getSite().portal_components.objectValues(portal_type='Tool Component'):
if tool_component.getValidationState() == 'validated':
module_name = tool_component.getReference()
# In case there are several versions, only load the 'default' one
if module_name not in seen_tool_component_set:
erp5.component.tool.find_load_module(module_name)
seen_tool_component_set.add(module_name)
# self is a FactoryDispatcher.
toolinit = self.toolinit
tl = []
for tool in toolinit.tools:
tl.append(tool.meta_type)
return addInstanceForm(addInstanceForm, self, REQUEST,
factory_action='manage_addTool',
factory_meta_type=toolinit.meta_type,
factory_product_name=toolinit.product_name,
factory_icon=toolinit.icon,
factory_types_list=tl,
factory_need_id=0)
Products.CMFCore.utils.manage_addToolForm = manage_addToolForm
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