From e1e2aea7fde2eba35edfd193773e0e05e7d384e6 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari <ayush.tiwari@nexedi.com> Date: Thu, 5 Jan 2017 10:00:36 +0000 Subject: [PATCH] bt5_prototype: BusinessPackage class defined --- product/ERP5/Document/BusinessPackage.py | 218 +++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 product/ERP5/Document/BusinessPackage.py diff --git a/product/ERP5/Document/BusinessPackage.py b/product/ERP5/Document/BusinessPackage.py new file mode 100644 index 0000000000..322050a19c --- /dev/null +++ b/product/ERP5/Document/BusinessPackage.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2017 Nexedi SARL and Contributors. All Rights Reserved. +# Ayush-Tiwari <ayush.tiwari@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. +# +############################################################################## + +import fnmatch, re +import transaction +from copy import deepcopy +from Acquisition import Implicit, aq_base, aq_inner, aq_parent +from Products.ERP5Type.XMLObject import XMLObject +from Products.ERP5Type import Permissions, PropertySheet, interfaces +from Products.ERP5.Document.BusinessTemplate import ObjectTemplateItem, BaseTemplateItem +from AccessControl import ClassSecurityInfo, Unauthorized, getSecurityManager +from Products.ERP5Type.Globals import Persistent, PersistentMapping + +_MARKER = [] + +def _delObjectWithoutHook(obj, id): + """OFS.ObjectManager._delObject without calling manage_beforeDelete.""" + ob = obj._getOb(id) + if obj._objects: + obj._objects = tuple([i for i in obj._objects if i['id'] != id]) + obj._delOb(id) + try: + ob._v__object_deleted__ = 1 + except: + pass + +def _recursiveRemoveUid(obj): + """Recusivly set uid to None, to prevent (un)indexing. + This is used to prevent unindexing real objects when we delete subobjects on + a copy of this object. + """ + if getattr(aq_base(obj), 'uid', _MARKER) is not _MARKER: + obj.uid = None + for subobj in obj.objectValues(): + _recursiveRemoveUid(subobj) + +class BusinessPackage(XMLObject): + """ + New implementation of Business Templates + """ + + meta_type = 'ERP5 Business Package' + portal_type = 'Business Package' + add_permission = Permissions.AddPortalContent + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.SimpleItem + , PropertySheet.CategoryCore + , PropertySheet.BusinessPackage + ) + + def _install(self): + pass + + security.declareProtected(Permissions.ManagePortal, 'install') + install = _install + + security.declareProtected(Permissions.ManagePortal, 'build') + def build(self): + """ + Should also export the objects from PathTemplateItem to their xml format + """ + self.storePathData() + for item in self._path_item: + item.export() + + security.declareProtected(Permissions.ManagePortal, 'storePathData') + def storePathData(self): + self._path_item = PathTemplatePackageItem(self._getTemplatePathList()) + + security.declareProtected(Permissions.ManagePortal, 'getTemplatePathList') + def _getTemplatePathList(self): + result = tuple(self.getTemplatePathList()) + if result is None: + result = () + return result + + security.declareProtected(Permissions.ManagePortal, 'export') + def export(self): + """ + Export the object + """ + pass + +class PathTemplatePackageItem(ObjectTemplateItem): + + def __init__(self, id_list, tool_id=None, **kw): + BaseTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw) + id_list = self._archive.keys() + self._archive.clear() + self._path_archive = PersistentMapping() + for id in id_list: + self._path_archive[id] = None + + def _resolvePath(self, folder, relative_url_list, id_list): + """ + This method calls itself recursively. + + The folder is the current object which contains sub-objects. + The list of ids are path components. If the list is empty, + the current folder is valid. + """ + if len(id_list) == 0: + return ['/'.join(relative_url_list)] + id = id_list[0] + if re.search('[\*\?\[\]]', id) is None: + # If the id has no meta character, do not have to check all objects. + obj = folder._getOb(id, None) + if obj is None: + raise AttributeError, "Could not resolve '%s' during business template processing." % id + return self._resolvePath(obj, relative_url_list + [id], id_list[1:]) + path_list = [] + for object_id in fnmatch.filter(folder.objectIds(), id): + if object_id != "": + path_list.extend(self._resolvePath( + folder._getOb(object_id), + relative_url_list + [object_id], id_list[1:])) + return path_list + + def build(self, context, **kw): + BaseTemplateItem.build(self, context, **kw) + p = context.getPortalObject() + keys = self._path_archive.keys() + keys.sort() + for path in keys: + include_subobjects = 0 + if path.endswith("**"): + include_subobjects = 1 + for relative_url in self._resolvePath(p, [], path.split('/')): + obj = p.unrestrictedTraverse(relative_url) + obj = obj._getCopy(context) + obj = obj.__of__(context) + _recursiveRemoveUid(obj) + id_list = obj.objectIds() + if hasattr(aq_base(obj), 'groups'): + # we must keep groups because it's ereased when we delete subobjects + groups = deepcopy(obj.groups) + if len(id_list) > 0: + if include_subobjects: + self.build_sub_objects(obj, id_list, relative_url) + for id_ in list(id_list): + _delObjectWithoutHook(obj, id_) + if hasattr(aq_base(obj), 'groups'): + obj.groups = groups + self._objects[relative_url] = obj + obj.wl_clearLocks() + + def install(self, context, *args, **kw): + super(PathTemplateItem, self).install(context, *args, **kw) + + # Regenerate local roles for all paths in this business template + p = context.getPortalObject() + portal_type_role_list_len_dict = {} + update_dict = defaultdict(list) + for path in self._objects: + obj = p.unrestrictedTraverse(path, None) + # Ignore any object without PortalType (non-ERP5 objects) + try: + portal_type = aq_base(obj).getPortalType() + except Exception, e: + pass + else: + if portal_type not in p.portal_types: + LOG("BusinessTemplate", WARNING, + "Could not update Local Roles as Portal Type '%s' could not " + "be found" % portal_type) + + continue + + if portal_type not in portal_type_role_list_len_dict: + portal_type_role_list_len_dict[portal_type] = \ + len(p.portal_types[portal_type].getRoleInformationList()) + + if portal_type_role_list_len_dict[portal_type]: + update_dict[portal_type].append(obj) + + if update_dict: + def updateLocalRolesOnDocument(): + for portal_type, obj_list in update_dict.iteritems(): + update = p.portal_types[portal_type].updateLocalRolesOnDocument + for obj in obj_list: + update(obj) + LOG("BusinessTemplate", INFO, + "Updated Local Roles for '%s' (%s)" + % (portal_type, obj.getRelativeUrl())) + transaction.get().addBeforeCommitHook(updateLocalRolesOnDocument) -- 2.30.9