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