From cb9a4462a53a282b904008e4a992310a169448c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20G=C3=B3rny?= <bartek@gorny.edu.pl> Date: Tue, 9 Jan 2007 10:12:47 +0000 Subject: [PATCH] portal_contributions - tool for handling creating content from file git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@11953 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5/Tool/ContributionTool.py | 240 ++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 product/ERP5/Tool/ContributionTool.py diff --git a/product/ERP5/Tool/ContributionTool.py b/product/ERP5/Tool/ContributionTool.py new file mode 100644 index 0000000000..5e68848617 --- /dev/null +++ b/product/ERP5/Tool/ContributionTool.py @@ -0,0 +1,240 @@ +############################################################################## +# +# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved. +# Jean-Paul Smets <jp@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + + +from AccessControl import ClassSecurityInfo +from Globals import InitializeClass, DTMLFile +from Products.ERP5Type.Tool.BaseTool import BaseTool +from Products.ERP5Type import Permissions +from Products.ERP5 import _dtmldir +from Products.ERP5.Document.BusinessTemplate import getChainByType +from zLOG import LOG +from DateTime import DateTime +from Acquisition import aq_base + +NO_DISCOVER_METADATA_KEY = '_v_no_discover_metadata' +USER_NAME_KEY = '_v_document_user_login' + +class ContributionTool(BaseTool): + """ + ContributionTool provides an abstraction layer to unify the contribution + of documents into an ERP5Site. + + ContributionTool is configured in portal_types in + such way that it can store Text, Spreadsheet, PDF, etc. + + The method to use is portal_contributions.newContent, which should receive + either a portal type or a file name from which type can be derived or a file from which + content type can be derived, otherwise it will fail. + + Configuration Scripts: + - ContributionTool_getPropertyDictFromFileName: receives file name and a + dict derived from filename by regular expression, and does any necesary + operations (e.g. mapping document type id onto a real portal_type). + """ + title = 'Contribution Tool' + id = 'portal_contributions' + meta_type = 'ERP5 Contribution Tool' + portal_type = 'Contribution Tool' + allowed_types = ('File', 'Image') # XXX Is this really needed ? + + # Declarative Security + security = ClassSecurityInfo() + + security.declareProtected(Permissions.ManagePortal, 'manage_overview' ) + manage_overview = DTMLFile( 'explainContributionTool', _dtmldir ) + + security.declarePrivate('findTypeName') + def findTypeName(self, name, ob): + """ + Finds the appropriate portal type based on the file name + or if necessary the content of ob + """ + # We should only consider those portal_types which share the + # same meta_type with the current object + valid_portal_type_list = [] + for pt in self.portal_types.objectValues(): + if pt.meta_type == ob.meta_type: + valid_portal_type_list.append(pt.id) + + # Check if the filename tells which portal_type this is + portal_type = self.getPropertyDictFromFileName(file_name).get('portal_type', None) + + # If it is still None, we need to read the document + # to check which of the candidates is suitable + if portal_type is None: + # The document is now responsible of telling all its properties + portal_type = ob.getPropertyDictFromContent().get('portal_type', None) + + if portal_type is None: + # We can not do anything anymore + return ob.portal_type + + if portal_type not in valid_portal_type_list: + # We will not be able to migrate ob to portal_type + return ob.portal_type + + return portal_type + + security.declareProtected(Permissions.AddPortalContent, 'newContent') + def newContent(self, id=None, portal_type=None, + discover_metadata=1, temp_object=0, + user_login=None, **kw): + """ + The newContent method is overriden to implement smart content + creation by detecting the portal type based on whatever information + was provided and finding out the most appropriate module to store + the content. + + user_login is the name under which the content will be created + XXX - Is this a security hole ? + + NOTE: + We always generate ID. So, we must prevent using the one + which we were provided. + """ + # Temp objects use the standard newContent from Folder + if temp_object: + # For temp_object creation, use the standard method + return BaseTool.newContent(self, id=id, portal_type=portal_type, temp_object=1, **kw) + + # Try to find the file_name + file = kw.get('file', None) + if file is not None: + file_name = file.name + else: + file_name = None + + # If the portal_type was provided, we can go faster + if portal_type is not None: + # We know the portal_type, let us find the module + module = self.getDefaultModule(portal_type) + + # And return a document + # NOTE: we use the module ID generator rather than the provided ID + document = module.newContent(portal_type=portal_type, **kw) + if discover_metadata: document.discoverMetadata(file_name=file_name, user_login=user_login) + return document + + # From here, there is no hope unless a file was provided + if file is None: + raise ValueError, "could not determine portal type" + + # So we will simulate WebDAV to get an empty object + # woith PUT_factory + ob = self.PUT_factory( file_name, None, None ) + + # Then put the file inside ourselves for a short while + BaseTool._setObject(self, name, ob) + document = self[name] + + # Then edit the document contents (so that upload can happen) + document._edit(**kw) + + # Remove the object from ourselves + self._delObject(name, ob) + + # Move it to where it belongs + if not discover_metadata: setattr(self, NO_DISCOVER_METADATA_KEY, 1) + setattr(ob, USER_NAME_KEY, user_login) + document = self._setObject(name, ob) + + # Reindex it and return it + document.immediateReindexObject() + return document + + security.declareProtected( Permissions.AddPortalContent, 'fromXML' ) + def newXML(self, xml): + """ + Create a new content based on XML data. This is intended for contributing + to ERP5 from another application. + """ + pass + + security.declareProtected(Permissions.ModifyPortalContent,'getPropertyDictFromFileName') + def getPropertyDictFromFileName(self, fname): + """ + Gets properties from filename. File name is parsed with a regular expression + set in preferences. The regexp should contain named groups. + """ + rx_src = self.portal_preferences.getPreferredDocumentFileNameRegularExpression() + if not rx_src: + return + rx_parse = re.compile() + if rx_parse is None: + return + dict = rx_parse.match(fname) + method = self._getTypeBasedMethod('getPropertyDictFromFileName', + fallback_script_id = 'ContributionTool_getPropertyDictFromFileName') + return method(fname, **dict) + + # WebDAV virtual folder support + def _setObject(self, name, ob, user_login=None): + """ + The strategy is to let NullResource.PUT do everything as + usual and at the last minute put the object in a different + location with a different portal type. This means that + NullResource.PUT creates an empty document with PUT_factory + then upload document data by invoking PUT on the empty + document and finally sets the object. By overriding _setObject + we get a chance to fix the portal_type of the document + (as long as the one we find is compatible) and move the + document to the appropriate module. + + content_type_registry must be set up so that an appropriate + portal_type with appropriate meta_type is found for every + kind of document. However, a different portal_type might + be used in the end. + + The ContributionTool instance must be configured in such + way that _verifyObjectPaste will return TRUE. + + Refer to: NullResource.PUT + """ + # Find the portal type based on file name and content + # We provide ob in the context of self to make sure scripting is possible + portal_type = self.findTypeName(name, ob.__of__(self)) + + # We know the portal_type, let us find the module + module = self.getDefaultModule(portal_type) + + # Set the object on the module and fix the portal_type and id + new_id = module.generateNewId() + ob.portal_type = portal_type + ob.id = new_id + module._setObject(new_id, ob) + + # We can now discover metadata unless NO_DISCOVER_METADATA_KEY was set on ob + document = module[new_id] + user_login = getattr(self, USER_NAME_KEY, None) + if not getattr(ob, NO_DISCOVER_METADATA_KEY, 0): document.discoverMetadata(file_name=name, user_login=user_login) + + # Return document to newContent method + return document + +InitializeClass(ContributionTool) -- 2.30.9