diff --git a/product/ERP5/bootstrap/erp5_core/PortalTypeTemplateItem/portal_types/Extension%20Component.xml b/product/ERP5/bootstrap/erp5_core/PortalTypeTemplateItem/portal_types/Extension%20Component.xml index 0d9fe94b59e9142a1bfb787417c52435994e6756..7fc1cfd037a82352a16bfbf39c9821aff4c81746 100644 --- a/product/ERP5/bootstrap/erp5_core/PortalTypeTemplateItem/portal_types/Extension%20Component.xml +++ b/product/ERP5/bootstrap/erp5_core/PortalTypeTemplateItem/portal_types/Extension%20Component.xml @@ -82,7 +82,7 @@ </item> <item> <key> <string>type_class</string> </key> - <value> <string>DocumentComponent</string> </value> + <value> <string>ExtensionComponent</string> </value> </item> <item> <key> <string>type_interface</string> </key> diff --git a/product/ERP5/bootstrap/erp5_core/bt/change_log b/product/ERP5/bootstrap/erp5_core/bt/change_log index 601d29024bacf76b420fb3e1ac7d6870b437bdbc..d60b3ca3777a640add9ae7079f65308d7d1026f0 100644 --- a/product/ERP5/bootstrap/erp5_core/bt/change_log +++ b/product/ERP5/bootstrap/erp5_core/bt/change_log @@ -1,3 +1,6 @@ +2012-01-19 arnaud.fontaine +* Extension Component has now its own class (meaningful for importing from filesystem). + 2012-01-18 arnaud.fontaine * Complete ComponentTool view. * Implement Extension Component. diff --git a/product/ERP5/bootstrap/erp5_core/bt/revision b/product/ERP5/bootstrap/erp5_core/bt/revision index edb471677f7d9720689b8c3fe0d2ce324931cb54..4f07dde62323fd97738215c3369ce36ec0875223 100644 --- a/product/ERP5/bootstrap/erp5_core/bt/revision +++ b/product/ERP5/bootstrap/erp5_core/bt/revision @@ -1 +1 @@ -40990 \ No newline at end of file +40991 \ No newline at end of file diff --git a/product/ERP5Type/Core/ExtensionComponent.py b/product/ERP5Type/Core/ExtensionComponent.py new file mode 100644 index 0000000000000000000000000000000000000000..6700f741e7b46b1deee31cd09d698dc44d0fa576 --- /dev/null +++ b/product/ERP5Type/Core/ExtensionComponent.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2012 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. +# +############################################################################## + +import os.path + +from Products.ERP5Type.Core.DocumentComponent import DocumentComponent +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions + +from zLOG import LOG, INFO + +class ExtensionComponent(DocumentComponent): + # CMF Type Definition + meta_type = 'ERP5 Extension Component' + portal_type = 'Extension Component' + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + security.declareProtected(Permissions.ModifyPortalContent, + 'importAllFromFilesystem') + @classmethod + def importAllFromFilesystem(cls, context): + """ + Try to import all Extensions as found in INSTANCEHOME/Extensions and + returns error as a dict if any + """ + from App.config import getConfiguration + extension_path_pattern = "%s%s%s/*" % (getConfiguration().instancehome, + os.path.sep, + 'Extensions') + + LOG("ERP5Type.Core.ExtensionComponent", INFO, + "Importing from %s" % extension_path_pattern) + + import glob + failed_import_dict = {} + for extension_path in glob.iglob(extension_path_pattern): + try: + cls.importFromFilesystem(context, extension_path) + except Exception, e: + failed_import_dict[extension_path] = str(e) + else: + LOG("ERP5Type.Core.ExtensionComponent", INFO, + "Imported %s" % extension_path) + + return failed_import_dict + + security.declareProtected(Permissions.ModifyPortalContent, + 'importFromFilesystem') + @classmethod + def importFromFilesystem(cls, context, path): + """ + Import an Extension from the given path into ZODB after checking that the + source code is valid + """ + with open(path) as extension_file: + source_code = extension_file.read() + + # Try to load it first + namespace_dict = {} + exec source_code in namespace_dict + + class_name = os.path.basename(path).replace('.py', '') + return context.newContent(id='erp5.component.extension.%s' % class_name, + # XXX-arnau: useless field? + reference=class_name, + text_content=source_code, + portal_type=cls.portal_type) diff --git a/product/ERP5Type/Tool/ComponentTool.py b/product/ERP5Type/Tool/ComponentTool.py index 37eccc766a25c5c13eee05df412fcac08c4d6584..a393bafd83795af58dbac5211ec14fe179cc3749 100644 --- a/product/ERP5Type/Tool/ComponentTool.py +++ b/product/ERP5Type/Tool/ComponentTool.py @@ -31,7 +31,7 @@ from AccessControl import ClassSecurityInfo from Products.ERP5Type.Tool.BaseTool import BaseTool from Products.ERP5Type import Permissions -from zLOG import LOG, INFO +from zLOG import LOG, INFO, WARNING class ComponentTool(BaseTool): """ @@ -75,3 +75,40 @@ class ComponentTool(BaseTool): "Global reset of %s.%s" % (module_name, name)) delattr(module, name) + + security.declareProtected(Permissions.ManagePortal, + 'createAllComponentFromFilesystem') + def createAllComponentFromFilesystem(self, erase_existing=False, + REQUEST=None): + """ + + XXX-arnau: only Extensions for now + """ + portal = self.getPortalObject() + + import erp5.portal_type + type_tool = portal.portal_types + failed_import_dict = {} + for content_portal_type in getattr(type_tool, + self.portal_type).getTypeAllowedContentTypeList(): + try: + failed_import_dict.update(getattr(erp5.portal_type, + content_portal_type).importAllFromFilesystem(self)) + except AttributeError: + LOG("ERP5Type.Tool.ComponentTool", WARNING, "Could not import %ss" % \ + content_portal_type) + + if REQUEST: + if failed_import_dict: + failed_import_formatted_list = [] + for name, error in failed_import_dict.iteritems(): + failed_import_formatted_list.append("%s (%s)" % (name, error)) + + message = "The following component could not be imported: %s" % \ + ', '.join(failed_import_formatted_list) + else: + message = "All components were successfully imported " \ + "from filesystem to ZODB." + + return self.Base_redirect('view', + keep_items={'portal_status_message': message})