diff --git a/bt5/erp5_mrp/DocumentTemplateItem/portal_components/document.erp5.ProductionSimulationRule.py b/bt5/erp5_mrp/DocumentTemplateItem/portal_components/document.erp5.ProductionSimulationRule.py
index ff25577ece45623af1627ece0db0c5e83b618451..7de68e2a6a160ed4c5f8252428bff60d9416d46a 100644
--- a/bt5/erp5_mrp/DocumentTemplateItem/portal_components/document.erp5.ProductionSimulationRule.py
+++ b/bt5/erp5_mrp/DocumentTemplateItem/portal_components/document.erp5.ProductionSimulationRule.py
@@ -1,5 +1,5 @@
 from Products.ERP5.Document.DeliverySimulationRule import DeliverySimulationRule
-from Products.ERP5.mixin.rule import MovementGeneratorMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 
 class ProductionSimulationRule(DeliverySimulationRule):
   """
diff --git a/bt5/erp5_web/DocumentTemplateItem/portal_components/document.erp5.StaticWebSection.py b/bt5/erp5_web/DocumentTemplateItem/portal_components/document.erp5.StaticWebSection.py
index c28d8bc18a60ef4725f3b0f26d308aee85503f0f..b8d88a6a126e88dde603d68a2b1218d87d622d61 100644
--- a/bt5/erp5_web/DocumentTemplateItem/portal_components/document.erp5.StaticWebSection.py
+++ b/bt5/erp5_web/DocumentTemplateItem/portal_components/document.erp5.StaticWebSection.py
@@ -31,7 +31,7 @@ from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
 from OFS.Traversable import NotFound
 
-from Products.ERP5.mixin.extensible_traversable import DocumentExtensibleTraversableMixin
+from Products.ERP5.mixin.document_extensible_traversable import DocumentExtensibleTraversableMixin
 from Products.ERP5.Document.WebSection import WebSection
 from Products.ERP5Type import Permissions
 
diff --git a/product/ERP5/Document/AcceptSolver.py b/product/ERP5/Document/AcceptSolver.py
index d0c5b3005b26a1f2cab3b85829251e6e1216f7d4..0b3f6fdfb796bf034834e5c9b3f855bcc808b138 100644
--- a/product/ERP5/Document/AcceptSolver.py
+++ b/product/ERP5/Document/AcceptSolver.py
@@ -29,7 +29,7 @@
 
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions
-from Products.ERP5.mixin.solver import ConfigurablePropertySolverMixin
+from Products.ERP5.mixin.configurable_property_solver import ConfigurablePropertySolverMixin
 
 class AcceptSolver(ConfigurablePropertySolverMixin):
   """Target solver that accepts the values from the decision on the prevision.
diff --git a/product/ERP5/Document/AdoptSolver.py b/product/ERP5/Document/AdoptSolver.py
index 17762d91d48852021ab15c7ebe1b1f90f1290e9f..9d995797869ed3320579a8799135d87bedf6aea3 100644
--- a/product/ERP5/Document/AdoptSolver.py
+++ b/product/ERP5/Document/AdoptSolver.py
@@ -29,7 +29,7 @@
 
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions
-from Products.ERP5.mixin.solver import ConfigurablePropertySolverMixin
+from Products.ERP5.mixin.configurable_property_solver import ConfigurablePropertySolverMixin
 
 class AdoptSolver(ConfigurablePropertySolverMixin):
   """Target solver that adopts the values from the prevision on the decision.
diff --git a/product/ERP5/Document/Delivery.py b/product/ERP5/Document/Delivery.py
index 35c82c89a15a639e14ebde4bd5d6a0bb460e5f6d..80c1590743bb7b75802660550cd2e2189cb2054c 100644
--- a/product/ERP5/Document/Delivery.py
+++ b/product/ERP5/Document/Delivery.py
@@ -41,7 +41,7 @@ from Products.ERP5Type.XMLObject import XMLObject
 from Products.ERP5.Document.ImmobilisationDelivery import ImmobilisationDelivery
 from Products.ERP5.mixin.amount_generator import AmountGeneratorMixin
 from Products.ERP5.mixin.composition import CompositionMixin
-from Products.ERP5.mixin.rule import SimulableMixin
+from Products.ERP5.mixin.simulable import SimulableMixin
 from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod, \
     unrestricted_apply
 from zLOG import LOG, PROBLEM
diff --git a/product/ERP5/Document/DeliveryRootSimulationRule.py b/product/ERP5/Document/DeliveryRootSimulationRule.py
index 44c507e84d06702a80639905e7de338f6b903092..2cf5e2bac3072502665d8f925483095db1ca743b 100644
--- a/product/ERP5/Document/DeliveryRootSimulationRule.py
+++ b/product/ERP5/Document/DeliveryRootSimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/DeliverySimulationRule.py b/product/ERP5/Document/DeliverySimulationRule.py
index 4cdbb868d7bb4e1495128b5c3065d504c71933c3..e96933ab543b28251398867b0051740935a7e935 100644
--- a/product/ERP5/Document/DeliverySimulationRule.py
+++ b/product/ERP5/Document/DeliverySimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/Document.py b/product/ERP5/Document/Document.py
index 5191be28a68bb805b2f4fae29a028616919a662b..d869715b50e55b5829c6480c41ae4d45c072ebc1 100644
--- a/product/ERP5/Document/Document.py
+++ b/product/ERP5/Document/Document.py
@@ -206,7 +206,7 @@ class DocumentConversionServerProxy():
   def __getattr__(self, attr):
     return partial(self._proxy_function, attr)
 
-from Products.ERP5.mixin.extensible_traversable import DocumentExtensibleTraversableMixin
+from Products.ERP5.mixin.document_extensible_traversable import DocumentExtensibleTraversableMixin
 
 class Document(DocumentExtensibleTraversableMixin, XMLObject, UrlMixin,
                CachedConvertableMixin, CrawlableMixin, TextConvertableMixin,
diff --git a/product/ERP5/Document/InvoiceSimulationRule.py b/product/ERP5/Document/InvoiceSimulationRule.py
index e9ee712e025ca24e6e1011793a64086c8474e897..436025b9f624557ee6125a8c875a10e486725298 100644
--- a/product/ERP5/Document/InvoiceSimulationRule.py
+++ b/product/ERP5/Document/InvoiceSimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/InvoiceTransactionSimulationRule.py b/product/ERP5/Document/InvoiceTransactionSimulationRule.py
index 1f32e253b6fbf7a79e36e66e9243bb4852527cfa..c134d989d8e46eb5be16d633312f41bc7d1f2a41 100644
--- a/product/ERP5/Document/InvoiceTransactionSimulationRule.py
+++ b/product/ERP5/Document/InvoiceTransactionSimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 from Products.ERP5.Document.PredicateMatrix import PredicateMatrix
diff --git a/product/ERP5/Document/OrderRootSimulationRule.py b/product/ERP5/Document/OrderRootSimulationRule.py
index a8e2e844c3253ad835384180433f4bfd6f1e64d4..ce184bc8a2776ac1b0dc319efe4a82f4e836c84e 100644
--- a/product/ERP5/Document/OrderRootSimulationRule.py
+++ b/product/ERP5/Document/OrderRootSimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/PaymentSimulationRule.py b/product/ERP5/Document/PaymentSimulationRule.py
index 84774e7ab189b19073df0673c9ce5988325341c6..f69cc8cace26cda4945279ff5df2f952d07e0f15 100644
--- a/product/ERP5/Document/PaymentSimulationRule.py
+++ b/product/ERP5/Document/PaymentSimulationRule.py
@@ -29,7 +29,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/SubscriptionItem.py b/product/ERP5/Document/SubscriptionItem.py
index d21842c982b4d89cfc43306db0dfd73b657af40b..808b3756e47c64dda433a147bb227ed0fac5ae2f 100644
--- a/product/ERP5/Document/SubscriptionItem.py
+++ b/product/ERP5/Document/SubscriptionItem.py
@@ -33,7 +33,8 @@ from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
 from Products.ERP5.Document.Item import Item
 from Products.ERP5.mixin.composition import CompositionMixin
-from Products.ERP5.mixin.rule import MovementGeneratorMixin, SimulableMixin
+from Products.ERP5.mixin.simulable import SimulableMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.periodicity import PeriodicityMixin
 from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
 from Products.ERP5Type.Base import Base
diff --git a/product/ERP5/Document/TradeModelSimulationRule.py b/product/ERP5/Document/TradeModelSimulationRule.py
index fe18562938311b122c3cccc91c56e07eb3c3a74d..78c8b3d1f851aace2d0a0e2e9f76e89942a2e216 100644
--- a/product/ERP5/Document/TradeModelSimulationRule.py
+++ b/product/ERP5/Document/TradeModelSimulationRule.py
@@ -30,7 +30,8 @@ import zope.interface
 from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/TransformationSimulationRule.py b/product/ERP5/Document/TransformationSimulationRule.py
index 68096d2572715cd76f446a99301cfee0348ce0d8..018b041653bdc9b55c4a736617794be2597d6cda 100644
--- a/product/ERP5/Document/TransformationSimulationRule.py
+++ b/product/ERP5/Document/TransformationSimulationRule.py
@@ -31,7 +31,8 @@ from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
 from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/TransformationSourcingSimulationRule.py b/product/ERP5/Document/TransformationSourcingSimulationRule.py
index f5493ab2729abfd167172cccc0829451710c7e74..3e1070cd98aa14fc61c66d84092d25f1b77fce91 100644
--- a/product/ERP5/Document/TransformationSourcingSimulationRule.py
+++ b/product/ERP5/Document/TransformationSourcingSimulationRule.py
@@ -30,7 +30,8 @@ import zope.interface
 from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
-from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
+from Products.ERP5.mixin.rule import RuleMixin
+from Products.ERP5.mixin.movement_generator import MovementGeneratorMixin
 from Products.ERP5.mixin.movement_collection_updater import \
      MovementCollectionUpdaterMixin
 
diff --git a/product/ERP5/Document/WebSection.py b/product/ERP5/Document/WebSection.py
index 34d0dcec8474c861a0e713e486ec4aca4a19342e..ab713a67eef527b10156ac582fbc0be32c4cd45b 100644
--- a/product/ERP5/Document/WebSection.py
+++ b/product/ERP5/Document/WebSection.py
@@ -30,7 +30,7 @@
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5.Document.Domain import Domain
-from Products.ERP5.mixin.extensible_traversable import DocumentExtensibleTraversableMixin
+from Products.ERP5.mixin.document_extensible_traversable import DocumentExtensibleTraversableMixin
 from Acquisition import aq_base, aq_inner
 from Products.ERP5Type.UnrestrictedMethod import unrestricted_apply
 from AccessControl import Unauthorized
diff --git a/product/ERP5/mixin/extensible_traversable.py b/product/ERP5/mixin/base_extensible_traversable.py
similarity index 67%
rename from product/ERP5/mixin/extensible_traversable.py
rename to product/ERP5/mixin/base_extensible_traversable.py
index c895e3e418ab122c9312d2df22727eef6f418403..b6019b20b823651157cebd6af26391513e8c10e6 100644
--- a/product/ERP5/mixin/extensible_traversable.py
+++ b/product/ERP5/mixin/base_extensible_traversable.py
@@ -27,26 +27,22 @@
 #
 ##############################################################################
 
+from warnings import warn
+from base64 import decodestring
+
 from zLOG import LOG
-from Acquisition import aq_base
-from Products.ERP5Type.Globals import get_request
-from AccessControl import Unauthorized
-from Products.ERP5Type.ExtensibleTraversable import ExtensibleTraversableMixIn
-from Products.ERP5Type.Cache import getReadOnlyTransactionCache
 from AccessControl import ClassSecurityInfo, getSecurityManager
 from AccessControl.SecurityManagement import newSecurityManager, setSecurityManager
+from Products.CMFCore.utils import getToolByName
+
+from Products.ERP5Type.ExtensibleTraversable import ExtensibleTraversableMixIn
+from Products.ERP5Type.Cache import getReadOnlyTransactionCache
 from Products.ERP5Type.Globals import InitializeClass
 from Products.ERP5Type import Permissions
-from Products.CMFCore.utils import getToolByName, _checkConditionalGET, _setCacheHeaders, _ViewEmulator
-from OFS.Image import File as OFSFile
-from warnings import warn
-from base64 import decodestring
-from Products.ERP5Type.UnrestrictedMethod import unrestricted_apply
-from Products.ERP5.Document.Document import ConversionError, NotConvertedError
+from Products.ERP5Type.Globals import get_request
 
 # XXX: these duplicate ones in ERP5.Document
 _MARKER = []
-EMBEDDED_FORMAT = '_embedded'
 
 class BaseExtensibleTraversableMixin(ExtensibleTraversableMixIn):
   """
@@ -170,61 +166,3 @@ class BaseExtensibleTraversableMixin(ExtensibleTraversableMixIn):
       return document.__of__(self)
 
 InitializeClass(BaseExtensibleTraversableMixin)
-
-class DocumentExtensibleTraversableMixin(BaseExtensibleTraversableMixin):
-  """
-  This class provides a implementation of IExtensibleTraversable for Document classed based documents.
-  """
-
-  def getExtensibleContent(self, request, name):
-    old_manager, user = self._forceIdentification(request)
-    # Next get the document per name
-    portal = self.getPortalObject()
-    document = self.getDocumentValue(name=name, portal=portal)
-    # restore original security context if there's a logged in user
-    if user is not None:
-      setSecurityManager(old_manager)
-    if document is not None:
-      document = aq_base(document.asContext(id=name, # Hide some properties to permit locating the original
-                                            original_container=document.getParentValue(),
-                                            original_id=document.getId(),
-                                            editable_absolute_url=document.absolute_url()))
-      return document.__of__(self)
-
-    # no document found for current user, still such document may exists
-    # in some cases user (like Anonymous) can not view document according to portal catalog
-    # but we may ask him to login if such a document exists
-    isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
-    if isAuthorizationForced is not None and isAuthorizationForced():
-      if unrestricted_apply(self.getDocumentValue, (name, portal)) is not None:
-        # force user to login as specified in Web Section
-        raise Unauthorized
-
-class OOoDocumentExtensibleTraversableMixin(BaseExtensibleTraversableMixin):
-  """
-  This class provides a implementation of IExtensibleTraversable for OOoDocument classed based documents.
-  """
-
-  def getExtensibleContent(self, request, name):
-    # Be sure that html conversion is done,
-    # as it is required to extract extensible content
-    old_manager, user = self._forceIdentification(request)
-    web_cache_kw = {'name': name,
-                    'format': EMBEDDED_FORMAT}
-    try:
-      self._convert(format='html')
-      view = _ViewEmulator().__of__(self)
-      # If we have a conditional get, set status 304 and return
-      # no content
-      if _checkConditionalGET(view, web_cache_kw):
-        return ''
-      # call caching policy manager.
-      _setCacheHeaders(view, web_cache_kw)
-      mime, data = self.getConversion(format=EMBEDDED_FORMAT, filename=name)
-      document = OFSFile(name, name, data, content_type=mime).__of__(self.aq_parent)
-    except (NotConvertedError, ConversionError, KeyError):
-      document = DocumentExtensibleTraversableMixin.getExtensibleContent(self, request, name)
-    # restore original security context if there's a logged in user
-    if user is not None:
-      setSecurityManager(old_manager)
-    return document
diff --git a/product/ERP5/mixin/configurable_property_solver.py b/product/ERP5/mixin/configurable_property_solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..14a9d3a0ce9d61ed781512b9948027c324194de7
--- /dev/null
+++ b/product/ERP5/mixin/configurable_property_solver.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets-Solanes <jp@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 zope.interface
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type.Globals import InitializeClass
+from Products.ERP5Type import Permissions, PropertySheet, interfaces
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5.mixin.solver import SolverMixin
+from Products.ERP5.mixin.configurable import ConfigurableMixin
+
+class ConfigurablePropertySolverMixin(SolverMixin,
+                                      ConfigurableMixin,
+                                      XMLObject):
+  """
+  Base class for Target Solvers that can be applied to many
+  solver-decisions of a solver process, and need to accumulate the
+  tested_property_list configuration among all solver-decisions
+  """
+
+  add_permission = Permissions.AddPortalContent
+  isIndexable = 0 # We do not want to fill the catalog with objects on which we need no reporting
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+  zope.interface.implements(interfaces.ISolver,
+                            interfaces.IConfigurable,)
+
+  # Default Properties
+  property_sheets = ( PropertySheet.Base
+                    , PropertySheet.XMLObject
+                    , PropertySheet.CategoryCore
+                    , PropertySheet.DublinCore
+                    , PropertySheet.TargetSolver
+                    )
+
+  def updateConfiguration(self, **kw):
+    # This method is called once for each 'Solver Decision' of a
+    # 'Solver Process' that maps into this solver for the same
+    # Simulation Movement, so we need to take care not to lose
+    # information by overwriting.
+    configuration = self._getConfigurationPropertyDict()
+    tested_property_list = configuration.get('tested_property_list')
+    if tested_property_list is not None:
+      tested_property_set = set(tested_property_list)
+      tested_property_set.update(kw.get('tested_property_list', ()))
+      kw['tested_property_list'] = list(tested_property_set)
+    super(ConfigurablePropertySolverMixin, self).updateConfiguration(**kw)
+
+  def getTestedPropertyList(self):
+    configuration_dict = self.getConfigurationPropertyDict()
+    tested_property_list = configuration_dict.get('tested_property_list')
+    if tested_property_list is None:
+      portal_type = self.getPortalObject().portal_types.getTypeInfo(self)
+      tested_property_list = portal_type.getTestedPropertyList()
+    return tested_property_list
+
+InitializeClass(ConfigurablePropertySolverMixin)
diff --git a/product/ERP5/mixin/document_extensible_traversable.py b/product/ERP5/mixin/document_extensible_traversable.py
new file mode 100644
index 0000000000000000000000000000000000000000..147fd6a61e181818169689de45aa1553735b1c16
--- /dev/null
+++ b/product/ERP5/mixin/document_extensible_traversable.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Ivan Tyagov <ivan@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 Acquisition import aq_base
+from AccessControl import Unauthorized
+from AccessControl.SecurityManagement import setSecurityManager
+from Products.ERP5.mixin.base_extensible_traversable import BaseExtensibleTraversableMixin
+from Products.ERP5Type.UnrestrictedMethod import unrestricted_apply
+
+class DocumentExtensibleTraversableMixin(BaseExtensibleTraversableMixin):
+  """
+  This class provides a implementation of IExtensibleTraversable for Document classed based documents.
+  """
+
+  def getExtensibleContent(self, request, name):
+    old_manager, user = self._forceIdentification(request)
+    # Next get the document per name
+    portal = self.getPortalObject()
+    document = self.getDocumentValue(name=name, portal=portal)
+    # restore original security context if there's a logged in user
+    if user is not None:
+      setSecurityManager(old_manager)
+    if document is not None:
+      document = aq_base(document.asContext(id=name, # Hide some properties to permit locating the original
+                                            original_container=document.getParentValue(),
+                                            original_id=document.getId(),
+                                            editable_absolute_url=document.absolute_url()))
+      return document.__of__(self)
+
+    # no document found for current user, still such document may exists
+    # in some cases user (like Anonymous) can not view document according to portal catalog
+    # but we may ask him to login if such a document exists
+    isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
+    if isAuthorizationForced is not None and isAuthorizationForced():
+      if unrestricted_apply(self.getDocumentValue, (name, portal)) is not None:
+        # force user to login as specified in Web Section
+        raise Unauthorized
diff --git a/product/ERP5/mixin/movement_generator.py b/product/ERP5/mixin/movement_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..a738ed8884c0d2b374f6814ca2a7df8d33935524
--- /dev/null
+++ b/product/ERP5/mixin/movement_generator.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#
+# 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.
+#
+##############################################################################
+
+class MovementGeneratorMixin(object):
+  """
+  This class provides a generic implementation of IMovementGenerator
+  which can be used together the Rule mixin class bellow. It does not
+  have any pretention to provide more than that.
+
+  TODO:
+    - _getInputMovementList is still not well defined. Should input
+      be an amount (_getInputAmountList) or a movement? This
+      requires careful thiking.
+  """
+  # Default values
+  _applied_rule = None
+  _rule = None
+  _trade_phase_list = None
+  _explanation = None
+
+  def __init__(self, applied_rule, explanation=None, rule=None, trade_phase_list=None):
+    self._trade_phase_list = trade_phase_list # XXX-JPS Why a list ?
+    self._applied_rule = applied_rule
+    if rule is None and applied_rule is not None:
+      self._rule = applied_rule.getSpecialiseValue()
+    else:
+      self._rule = rule # for rule specific stuff
+    if explanation is None:
+      self._explanation = applied_rule
+    else:
+      # A good example of explicit explanation can be getRootExplanationLineValue
+      # since different lines could have different dates
+      # such an explicit root explanation only works if
+      # indexing of simulation has already happened
+      self._explanation = explanation
+    # XXX-JPS handle delay_mode
+
+  # Implementation of IMovementGenerator
+  def getGeneratedMovementList(self, movement_list=None, rounding=False):
+    """
+    Returns a list of movements generated by that rule.
+
+    movement_list - optional IMovementList which can be passed explicitely
+
+    rounding - boolean argument, which controls if rounding shall be applied on
+               generated movements or not
+
+    NOTE:
+      - implement rounding appropriately (True or False seems
+        simplistic)
+    """
+    # Default implementation below can be overriden by subclasses
+    # however it should be generic enough not to be overriden
+    # by most classes
+    # Results will be appended to result
+    result = []
+    # Build a list of movement and business path
+    input_movement_list = self._getInputMovementList(
+                            movement_list=movement_list, rounding=rounding)
+    for input_movement in input_movement_list:
+      # Merge movement and business path properties (core implementation)
+      # Lookup Business Process through composition (NOT UNION)
+      business_process = input_movement.asComposedDocument()
+      explanation = self._applied_rule # We use applied rule as local explanation
+      trade_phase = self._getTradePhaseList(input_movement, business_process) # XXX-JPS not convenient to handle
+      update_property_dict = self._getUpdatePropertyDict(input_movement)
+      result.extend(business_process.getTradePhaseMovementList(explanation, input_movement,
+                                                 trade_phase=trade_phase, delay_mode=None,
+                                                 update_property_dict=update_property_dict))
+
+    # And return list of generated movements
+    return result
+
+  def _getUpdatePropertyDict(self, input_movement):
+    # XXX Wouldn't it better to return {} or {'delivery': None} ?
+    #     Below code is mainly for root applied rules.
+    #     Other movement generators usually want to reset delivery.
+    return {'delivery': input_movement.getRelativeUrl()}
+
+  def _getTradePhaseList(self, input_movement, business_process): # XXX-JPS WEIRD
+    if self._trade_phase_list:
+      return self._trade_phase_list
+    if self._rule is not None:
+      trade_phase_list = self._rule.getTradePhaseList()
+      if trade_phase_list:
+        return trade_phase_list
+    return input_movement.getTradePhaseList() or \
+      business_process.getTradePhaseList()
+
+  def _getInputMovementList(self, movement_list=None, rounding=None): #XXX-JPS should it be amount or movement ?
+    raise NotImplementedError
+    # Default implementation takes amounts ?
+    # Use TradeModelRuleMovementGenerator._getInputMovementList as default implementation
+    # and potentially use trade phase for that.... as a way to filter out
diff --git a/product/ERP5/mixin/ooo_document_extensible_traversable.py b/product/ERP5/mixin/ooo_document_extensible_traversable.py
new file mode 100644
index 0000000000000000000000000000000000000000..51352b042d80bff9a3ba3307a3c09fc26e929001
--- /dev/null
+++ b/product/ERP5/mixin/ooo_document_extensible_traversable.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#                    Ivan Tyagov <ivan@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.SecurityManagement import setSecurityManager
+from Products.CMFCore.utils import _checkConditionalGET, _setCacheHeaders, _ViewEmulator
+from OFS.Image import File as OFSFile
+from Products.ERP5.Document.Document import ConversionError, NotConvertedError
+from Products.ERP5.mixin.base_extensible_traversable import BaseExtensibleTraversableMixin
+from Products.ERP5.mixin.document_extensible_traversable import DocumentExtensibleTraversableMixin
+
+# XXX: these duplicate ones in ERP5.Document
+EMBEDDED_FORMAT = '_embedded'
+
+class OOoDocumentExtensibleTraversableMixin(BaseExtensibleTraversableMixin):
+  """
+  This class provides a implementation of IExtensibleTraversable for OOoDocument classed based documents.
+  """
+
+  def getExtensibleContent(self, request, name):
+    # Be sure that html conversion is done,
+    # as it is required to extract extensible content
+    old_manager, user = self._forceIdentification(request)
+    web_cache_kw = {'name': name,
+                    'format': EMBEDDED_FORMAT}
+    try:
+      self._convert(format='html')
+      view = _ViewEmulator().__of__(self)
+      # If we have a conditional get, set status 304 and return
+      # no content
+      if _checkConditionalGET(view, web_cache_kw):
+        return ''
+      # call caching policy manager.
+      _setCacheHeaders(view, web_cache_kw)
+      mime, data = self.getConversion(format=EMBEDDED_FORMAT, filename=name)
+      document = OFSFile(name, name, data, content_type=mime).__of__(self.aq_parent)
+    except (NotConvertedError, ConversionError, KeyError):
+      document = DocumentExtensibleTraversableMixin.getExtensibleContent(self, request, name)
+    # restore original security context if there's a logged in user
+    if user is not None:
+      setSecurityManager(old_manager)
+    return document
diff --git a/product/ERP5/mixin/rule.py b/product/ERP5/mixin/rule.py
index 9750d0fbb3dd02e7b9d471daca1519bee203dcb9..8ad3a3d4d0865cf204e25c0b712f3bc6924e3a53 100644
--- a/product/ERP5/mixin/rule.py
+++ b/product/ERP5/mixin/rule.py
@@ -26,20 +26,12 @@
 #
 ##############################################################################
 
-import transaction
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type.Globals import InitializeClass
-from Acquisition import aq_base
 from Products.ERP5Type import Permissions, interfaces
-from Products.ERP5Type.Base import Base
 from Products.ERP5Type.Core.Predicate import Predicate
-from Products.ERP5Type.Errors import SimulationError
-from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
 from Products.ERP5.ExpandPolicy import policy_dict
-from Products.ERP5.MovementCollectionDiff import _getPropertyAndCategoryList
-
-from zLOG import LOG
 
 def _compare(tester_list, prevision_movement, decision_movement):
   for tester in tester_list:
@@ -47,99 +39,6 @@ def _compare(tester_list, prevision_movement, decision_movement):
       return False
   return True
 
-class MovementGeneratorMixin(object):
-  """
-  This class provides a generic implementation of IMovementGenerator
-  which can be used together the Rule mixin class bellow. It does not
-  have any pretention to provide more than that.
-
-  TODO:
-    - _getInputMovementList is still not well defined. Should input
-      be an amount (_getInputAmountList) or a movement? This
-      requires careful thiking.
-  """
-  # Default values
-  _applied_rule = None
-  _rule = None
-  _trade_phase_list = None
-  _explanation = None
-
-  def __init__(self, applied_rule, explanation=None, rule=None, trade_phase_list=None):
-    self._trade_phase_list = trade_phase_list # XXX-JPS Why a list ?
-    self._applied_rule = applied_rule
-    if rule is None and applied_rule is not None:
-      self._rule = applied_rule.getSpecialiseValue()
-    else:
-      self._rule = rule # for rule specific stuff
-    if explanation is None:
-      self._explanation = applied_rule
-    else:
-      # A good example of explicit explanation can be getRootExplanationLineValue
-      # since different lines could have different dates
-      # such an explicit root explanation only works if
-      # indexing of simulation has already happened
-      self._explanation = explanation
-    # XXX-JPS handle delay_mode
-
-  # Implementation of IMovementGenerator
-  def getGeneratedMovementList(self, movement_list=None, rounding=False):
-    """
-    Returns a list of movements generated by that rule.
-
-    movement_list - optional IMovementList which can be passed explicitely
-
-    rounding - boolean argument, which controls if rounding shall be applied on
-               generated movements or not
-
-    NOTE:
-      - implement rounding appropriately (True or False seems
-        simplistic)
-    """
-    # Default implementation below can be overriden by subclasses
-    # however it should be generic enough not to be overriden
-    # by most classes
-    # Results will be appended to result
-    result = []
-    # Build a list of movement and business path
-    input_movement_list = self._getInputMovementList(
-                            movement_list=movement_list, rounding=rounding)
-    for input_movement in input_movement_list:
-      # Merge movement and business path properties (core implementation)
-      # Lookup Business Process through composition (NOT UNION)
-      business_process = input_movement.asComposedDocument()
-      explanation = self._applied_rule # We use applied rule as local explanation
-      trade_phase = self._getTradePhaseList(input_movement, business_process) # XXX-JPS not convenient to handle
-      update_property_dict = self._getUpdatePropertyDict(input_movement)
-      result.extend(business_process.getTradePhaseMovementList(explanation, input_movement,
-                                                 trade_phase=trade_phase, delay_mode=None,
-                                                 update_property_dict=update_property_dict))
-
-    # And return list of generated movements
-    return result
-
-  def _getUpdatePropertyDict(self, input_movement):
-    # XXX Wouldn't it better to return {} or {'delivery': None} ?
-    #     Below code is mainly for root applied rules.
-    #     Other movement generators usually want to reset delivery.
-    return {'delivery': input_movement.getRelativeUrl()}
-
-  def _getTradePhaseList(self, input_movement, business_process): # XXX-JPS WEIRD
-    if self._trade_phase_list:
-      return self._trade_phase_list
-    if self._rule is not None:
-      trade_phase_list = self._rule.getTradePhaseList()
-      if trade_phase_list:
-        return trade_phase_list
-    return input_movement.getTradePhaseList() or \
-      business_process.getTradePhaseList()
-
-  def _getInputMovementList(self, movement_list=None, rounding=None): #XXX-JPS should it be amount or movement ?
-    raise NotImplementedError
-    # Default implementation takes amounts ?
-    # Use TradeModelRuleMovementGenerator._getInputMovementList as default implementation
-    # and potentially use trade phase for that.... as a way to filter out
-
-
 class RuleMixin(Predicate):
   """
   Provides generic methods and helper methods to implement
@@ -479,142 +378,3 @@ class RuleMixin(Predicate):
           movement_collection_diff.addNewMovement(new_movement)
 
 InitializeClass(RuleMixin)
-
-class SimulableMixin(Base):
-  security = ClassSecurityInfo()
-
-  def updateSimulation(self, **kw):
-    """Create/update related simulation trees by activity
-
-    This method is used to maintain related objects in simulation trees:
-    - hiding complexity of activity dependencies
-    - avoiding duplicate work
-
-    Repeated calls of this method for the same delivery will result in a single
-    call to _updateSimulation. Grouping may happen at the end of the transaction
-    or by the grouping method.
-
-    See _updateSimulation for accepted parameters.
-    """
-    tv = getTransactionalVariable()
-    key = 'SimulableMixin.updateSimulation', self.getUid()
-    item_list = kw.items()
-    try:
-      kw, ignore = tv[key]
-      kw.update(item_list)
-    except KeyError:
-      ignore_key = key + ('ignore',)
-      ignore = tv.pop(ignore_key, set())
-      tv[key] = kw, ignore
-      def before_commit():
-        if kw:
-          path = self.getPath()
-          if aq_base(self.unrestrictedTraverse(path, None)) is aq_base(self):
-            self.activate(
-              activity='SQLQueue',
-              group_method_id='portal_rules/updateSimulation',
-              tag='build:' + path,
-              priority=3,
-              )._updateSimulation(**kw)
-        del tv[key]
-        ignore.update(kw)
-        tv[ignore_key] = ignore
-      transaction.get().addBeforeCommitHook(before_commit)
-    for k, v in item_list:
-      if not v:
-        ignore.add(k)
-      elif k not in ignore:
-        continue
-      del kw[k]
-
-  def _updateSimulation(self, create_root=0, expand_root=0,
-                              expand_related=0, index_related=0):
-    """
-    Depending on set parameters, this method will:
-      create_root    -- if a root applied rule is missing, create and expand it
-      expand_root    -- expand related root applied rule,
-                        create it before if missing
-      expand_related -- expand related simulation movements
-      index_related  -- reindex related simulation movements (recursively)
-    """
-    if create_root or expand_root:
-      applied_rule = self._getRootAppliedRule()
-      if applied_rule is None:
-        applied_rule = self._createRootAppliedRule()
-        expand_root = applied_rule is not None
-    activate_kw = {'tag': 'build:'+self.getPath()}
-    if expand_root:
-      applied_rule.expand(activate_kw=activate_kw)
-    else:
-      applied_rule = None
-    if expand_related:
-      for movement in self._getAllRelatedSimulationMovementList():
-        movement = movement.getObject()
-        if not movement.aq_inContextOf(applied_rule):
-          # XXX: make sure this will also reindex of all sub-objects recursively
-          movement.expand(activate_kw=activate_kw)
-    elif index_related:
-      for movement in self._getAllRelatedSimulationMovementList():
-        movement = movement.getObject()
-        if not movement.aq_inContextOf(applied_rule):
-          movement.recursiveReindexObject(activate_kw=activate_kw)
-
-  security.declareProtected( Permissions.AccessContentsInformation,
-                             'getRuleReference')
-  def getRuleReference(self):
-    """Returns an appropriate rule reference
-
-    XXX: Using reference to select a rule (for a root applied rule) is wrong
-         and should be replaced by predicate and workflow state.
-    """
-    method = self._getTypeBasedMethod('getRuleReference')
-    if method is None:
-      raise SimulationError("Missing type-based 'getRuleReference' script for "
-                            + repr(self))
-    return method()
-
-  def _getRootAppliedRule(self):
-    """Get related root applied rule if it exists"""
-    applied_rule_list = self.getCausalityRelatedValueList(
-        portal_type='Applied Rule')
-    if len(applied_rule_list) == 1:
-      return applied_rule_list[0]
-    elif applied_rule_list:
-      raise SimulationError('%r has more than one applied rule.' % self)
-
-  def _createRootAppliedRule(self):
-    """Create a root applied rule"""
-    # XXX: Consider moving this first test to Delivery
-    if self.isSimulated():
-      # No need to have a root applied rule
-      # if we are already in the simulation process
-      return
-    rule_reference = self.getRuleReference()
-    if rule_reference:
-      portal = self.getPortalObject()
-      rule_list = portal.portal_catalog.unrestrictedSearchResults(
-        portal_type=portal.getPortalRuleTypeList(),
-        validation_state="validated", reference=rule_reference,
-        sort_on='version', sort_order='descending')
-      if rule_list:
-        applied_rule = rule_list[0].constructNewAppliedRule(
-          portal.portal_simulation, is_indexable=False)
-        applied_rule._setCausalityValue(self)
-        del applied_rule.isIndexable
-        # To prevent duplicate root Applied Rule, we reindex immediately and
-        # lock ZODB, and we rely on the fact that ZODB is committed after
-        # catalog. This way, we guarantee the catalog is up-to-date as soon as
-        # ZODB is unlocked.
-        applied_rule.immediateReindexObject()
-        self.serialize() # prevent duplicate root Applied Rule
-        return applied_rule
-      raise SimulationError("No such rule as %r is found" % rule_reference)
-
-  security.declarePrivate('manage_beforeDelete')
-  def manage_beforeDelete(self, item, container):
-    """Delete related Applied Rule"""
-    for o in self.getCausalityRelatedValueList(portal_type='Applied Rule'):
-      o.getParentValue().deleteContent(o.getId())
-    super(SimulableMixin, self).manage_beforeDelete(item, container)
-
-InitializeClass(SimulableMixin)
diff --git a/product/ERP5/mixin/simulable.py b/product/ERP5/mixin/simulable.py
new file mode 100644
index 0000000000000000000000000000000000000000..6dfc7fad60bbf13d06f4227357782eeaa0d94b39
--- /dev/null
+++ b/product/ERP5/mixin/simulable.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
+#
+# 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 transaction
+from Acquisition import aq_base
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions
+from Products.ERP5Type.Globals import InitializeClass
+from Products.ERP5Type.Base import Base
+from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
+from Products.ERP5Type.Errors import SimulationError
+
+class SimulableMixin(Base):
+  security = ClassSecurityInfo()
+
+  def updateSimulation(self, **kw):
+    """Create/update related simulation trees by activity
+
+    This method is used to maintain related objects in simulation trees:
+    - hiding complexity of activity dependencies
+    - avoiding duplicate work
+
+    Repeated calls of this method for the same delivery will result in a single
+    call to _updateSimulation. Grouping may happen at the end of the transaction
+    or by the grouping method.
+
+    See _updateSimulation for accepted parameters.
+    """
+    tv = getTransactionalVariable()
+    key = 'SimulableMixin.updateSimulation', self.getUid()
+    item_list = kw.items()
+    try:
+      kw, ignore = tv[key]
+      kw.update(item_list)
+    except KeyError:
+      ignore_key = key + ('ignore',)
+      ignore = tv.pop(ignore_key, set())
+      tv[key] = kw, ignore
+      def before_commit():
+        if kw:
+          path = self.getPath()
+          if aq_base(self.unrestrictedTraverse(path, None)) is aq_base(self):
+            self.activate(
+              activity='SQLQueue',
+              group_method_id='portal_rules/updateSimulation',
+              tag='build:' + path,
+              priority=3,
+              )._updateSimulation(**kw)
+        del tv[key]
+        ignore.update(kw)
+        tv[ignore_key] = ignore
+      transaction.get().addBeforeCommitHook(before_commit)
+    for k, v in item_list:
+      if not v:
+        ignore.add(k)
+      elif k not in ignore:
+        continue
+      del kw[k]
+
+  def _updateSimulation(self, create_root=0, expand_root=0,
+                              expand_related=0, index_related=0):
+    """
+    Depending on set parameters, this method will:
+      create_root    -- if a root applied rule is missing, create and expand it
+      expand_root    -- expand related root applied rule,
+                        create it before if missing
+      expand_related -- expand related simulation movements
+      index_related  -- reindex related simulation movements (recursively)
+    """
+    if create_root or expand_root:
+      applied_rule = self._getRootAppliedRule()
+      if applied_rule is None:
+        applied_rule = self._createRootAppliedRule()
+        expand_root = applied_rule is not None
+    activate_kw = {'tag': 'build:'+self.getPath()}
+    if expand_root:
+      applied_rule.expand(activate_kw=activate_kw)
+    else:
+      applied_rule = None
+    if expand_related:
+      for movement in self._getAllRelatedSimulationMovementList():
+        movement = movement.getObject()
+        if not movement.aq_inContextOf(applied_rule):
+          # XXX: make sure this will also reindex of all sub-objects recursively
+          movement.expand(activate_kw=activate_kw)
+    elif index_related:
+      for movement in self._getAllRelatedSimulationMovementList():
+        movement = movement.getObject()
+        if not movement.aq_inContextOf(applied_rule):
+          movement.recursiveReindexObject(activate_kw=activate_kw)
+
+  security.declareProtected( Permissions.AccessContentsInformation,
+                             'getRuleReference')
+  def getRuleReference(self):
+    """Returns an appropriate rule reference
+
+    XXX: Using reference to select a rule (for a root applied rule) is wrong
+         and should be replaced by predicate and workflow state.
+    """
+    method = self._getTypeBasedMethod('getRuleReference')
+    if method is None:
+      raise SimulationError("Missing type-based 'getRuleReference' script for "
+                            + repr(self))
+    return method()
+
+  def _getRootAppliedRule(self):
+    """Get related root applied rule if it exists"""
+    applied_rule_list = self.getCausalityRelatedValueList(
+        portal_type='Applied Rule')
+    if len(applied_rule_list) == 1:
+      return applied_rule_list[0]
+    elif applied_rule_list:
+      raise SimulationError('%r has more than one applied rule.' % self)
+
+  def _createRootAppliedRule(self):
+    """Create a root applied rule"""
+    # XXX: Consider moving this first test to Delivery
+    if self.isSimulated():
+      # No need to have a root applied rule
+      # if we are already in the simulation process
+      return
+    rule_reference = self.getRuleReference()
+    if rule_reference:
+      portal = self.getPortalObject()
+      rule_list = portal.portal_catalog.unrestrictedSearchResults(
+        portal_type=portal.getPortalRuleTypeList(),
+        validation_state="validated", reference=rule_reference,
+        sort_on='version', sort_order='descending')
+      if rule_list:
+        applied_rule = rule_list[0].constructNewAppliedRule(
+          portal.portal_simulation, is_indexable=False)
+        applied_rule._setCausalityValue(self)
+        del applied_rule.isIndexable
+        # To prevent duplicate root Applied Rule, we reindex immediately and
+        # lock ZODB, and we rely on the fact that ZODB is committed after
+        # catalog. This way, we guarantee the catalog is up-to-date as soon as
+        # ZODB is unlocked.
+        applied_rule.immediateReindexObject()
+        self.serialize() # prevent duplicate root Applied Rule
+        return applied_rule
+      raise SimulationError("No such rule as %r is found" % rule_reference)
+
+  security.declarePrivate('manage_beforeDelete')
+  def manage_beforeDelete(self, item, container):
+    """Delete related Applied Rule"""
+    for o in self.getCausalityRelatedValueList(portal_type='Applied Rule'):
+      o.getParentValue().deleteContent(o.getId())
+    super(SimulableMixin, self).manage_beforeDelete(item, container)
+
+InitializeClass(SimulableMixin)
diff --git a/product/ERP5/mixin/solver.py b/product/ERP5/mixin/solver.py
index a9152dcc867617b60530385717384cc6acbb1a8a..cf19c5b0001fea913d1ecc2aba413cc69adcdc08 100644
--- a/product/ERP5/mixin/solver.py
+++ b/product/ERP5/mixin/solver.py
@@ -30,10 +30,8 @@
 import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type.Globals import InitializeClass
-from Products.ERP5Type import Permissions, PropertySheet, interfaces
+from Products.ERP5Type import Permissions, interfaces
 from Products.ERP5Type.UnrestrictedMethod import super_user
-from Products.ERP5Type.XMLObject import XMLObject
-from Products.ERP5.mixin.configurable import ConfigurableMixin
 
 class SolverMixin(object):
   """
@@ -73,53 +71,3 @@ class SolverMixin(object):
     return solver_list
 
 InitializeClass(SolverMixin)
-
-class ConfigurablePropertySolverMixin(SolverMixin,
-                                      ConfigurableMixin,
-                                      XMLObject):
-  """
-  Base class for Target Solvers that can be applied to many
-  solver-decisions of a solver process, and need to accumulate the
-  tested_property_list configuration among all solver-decisions
-  """
-
-  add_permission = Permissions.AddPortalContent
-  isIndexable = 0 # We do not want to fill the catalog with objects on which we need no reporting
-
-  # Declarative security
-  security = ClassSecurityInfo()
-  security.declareObjectProtected(Permissions.AccessContentsInformation)
-
-  zope.interface.implements(interfaces.ISolver,
-                            interfaces.IConfigurable,)
-
-  # Default Properties
-  property_sheets = ( PropertySheet.Base
-                    , PropertySheet.XMLObject
-                    , PropertySheet.CategoryCore
-                    , PropertySheet.DublinCore
-                    , PropertySheet.TargetSolver
-                    )
-
-  def updateConfiguration(self, **kw):
-    # This method is called once for each 'Solver Decision' of a
-    # 'Solver Process' that maps into this solver for the same
-    # Simulation Movement, so we need to take care not to lose
-    # information by overwriting.
-    configuration = self._getConfigurationPropertyDict()
-    tested_property_list = configuration.get('tested_property_list')
-    if tested_property_list is not None:
-      tested_property_set = set(tested_property_list)
-      tested_property_set.update(kw.get('tested_property_list', ()))
-      kw['tested_property_list'] = list(tested_property_set)
-    super(ConfigurablePropertySolverMixin, self).updateConfiguration(**kw)
-
-  def getTestedPropertyList(self):
-    configuration_dict = self.getConfigurationPropertyDict()
-    tested_property_list = configuration_dict.get('tested_property_list')
-    if tested_property_list is None:
-      portal_type = self.getPortalObject().portal_types.getTypeInfo(self)
-      tested_property_list = portal_type.getTestedPropertyList()
-    return tested_property_list
-
-InitializeClass(ConfigurablePropertySolverMixin)
diff --git a/product/ERP5Configurator/Document/SecurityCategoryMappingConfiguratorItem.py b/product/ERP5Configurator/Document/SecurityCategoryMappingConfiguratorItem.py
index 044efdd95734feffbf0fb2810c4568554e000083..a28de84149700e4cae0cb3cf90ed44e3e709a81c 100644
--- a/product/ERP5Configurator/Document/SecurityCategoryMappingConfiguratorItem.py
+++ b/product/ERP5Configurator/Document/SecurityCategoryMappingConfiguratorItem.py
@@ -30,7 +30,7 @@ import zope.interface
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, interfaces
 from Products.ERP5Type.XMLObject import XMLObject
-from Products.ERP5Configurator.mixin.configurator_item import \
+from Products.ERP5Configurator.mixin.skin_configurator_item import \
                                        SkinConfiguratorItemMixin
 
 class SecurityCategoryMappingConfiguratorItem(SkinConfiguratorItemMixin,
diff --git a/product/ERP5Configurator/mixin/configurator_item.py b/product/ERP5Configurator/mixin/configurator_item.py
index 185cfafdd746d3e3000949d277faae59e53ce2bc..0d3e62c14f384dac5e13ff38cfed12dadce71ace 100644
--- a/product/ERP5Configurator/mixin/configurator_item.py
+++ b/product/ERP5Configurator/mixin/configurator_item.py
@@ -29,9 +29,7 @@
 ##############################################################################
 
 from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
-from Products.ERP5Type.Base import Base
 from zLOG import LOG, INFO
-import time
 
 class ConfiguratorItemMixin:
   """ This is the base class for all configurator item. """
@@ -64,53 +62,3 @@ class ConfiguratorItemMixin:
     current_template_path_list = list(bt5_obj.getTemplatePathList())
     current_template_path_list.extend(template_path_list)
     bt5_obj.edit(template_path_list=current_template_path_list)
-
-
-class SkinConfiguratorItemMixin(ConfiguratorItemMixin):
-  """ Mixin which allows to create python scripts and/or skin
-      elements during the configuration.
-  """
-
-  def install(self, skinfolder, business_configuration):
-    """
-    """
-    bt5_obj = business_configuration.getSpecialiseValue()
-    if bt5_obj is None:
-      LOG('ConfiguratorItem', INFO,
-          'Unable to find related business template to %s' % \
-            business_configuration.getRelativeUrl())
-      return
-
-    template_skin_id_list = list(bt5_obj.getTemplateSkinIdList())
-    if skinfolder.getId() not in template_skin_id_list:
-      template_skin_id_list.append(skinfolder.getId())
-    bt5_obj.edit(template_skin_id_list=template_skin_id_list)
-
-  def _createSkinFolder(self, folder_id="custom"):
-    """ Creates a new skin folder id if it do not exists and
-        update Skin information """
-    folder = getattr(self.portal_skins, folder_id, None)
-    if folder is not None:
-      return folder
-
-    folder = self.portal_skins.manage_addProduct['OFSP'].manage_addFolder(folder_id)
-    # Register on all skin selections.
-    raise NotImplementedError
-
-  def _createZODBPythonScript(self, container, script_id, script_params,
-                            script_content):
-    """Creates a Python script `script_id` in the given `container`, with
-    `script_params` and `script_content`.
-
-    If the container already contains an object with id `script_id`, this
-    object is removed first.
-    """
-    if script_id in container.objectIds():
-       container.manage_delObjects([script_id])
-
-    container.manage_addProduct['PythonScripts']\
-                  .manage_addPythonScript(id = script_id)
-    script = container._getOb(script_id)
-    script.ZPythonScript_edit(script_params, script_content)
-    container.portal_url.getPortalObject().changeSkin(None)
-    return script
diff --git a/product/ERP5Configurator/mixin/skin_configurator_item.py b/product/ERP5Configurator/mixin/skin_configurator_item.py
new file mode 100644
index 0000000000000000000000000000000000000000..cce5ea7168366f9b7d93103b9f3e441cbbc1d23e
--- /dev/null
+++ b/product/ERP5Configurator/mixin/skin_configurator_item.py
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2006-2012 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Romain Courteaud <romain@nexedi.com>
+#                    Ivan Tyagov <ivan@nexedi.com>
+#                    Rafael Monnerat <rafael@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 zLOG import LOG, INFO
+from Products.ERP5Configurator.mixin.configurator_item import ConfiguratorItemMixin
+
+class SkinConfiguratorItemMixin(ConfiguratorItemMixin):
+  """ Mixin which allows to create python scripts and/or skin
+      elements during the configuration.
+  """
+
+  def install(self, skinfolder, business_configuration):
+    """
+    """
+    bt5_obj = business_configuration.getSpecialiseValue()
+    if bt5_obj is None:
+      LOG('ConfiguratorItem', INFO,
+          'Unable to find related business template to %s' % \
+            business_configuration.getRelativeUrl())
+      return
+
+    template_skin_id_list = list(bt5_obj.getTemplateSkinIdList())
+    if skinfolder.getId() not in template_skin_id_list:
+      template_skin_id_list.append(skinfolder.getId())
+    bt5_obj.edit(template_skin_id_list=template_skin_id_list)
+
+  def _createSkinFolder(self, folder_id="custom"):
+    """ Creates a new skin folder id if it do not exists and
+        update Skin information """
+    folder = getattr(self.portal_skins, folder_id, None)
+    if folder is not None:
+      return folder
+
+    folder = self.portal_skins.manage_addProduct['OFSP'].manage_addFolder(folder_id)
+    # Register on all skin selections.
+    raise NotImplementedError
+
+  def _createZODBPythonScript(self, container, script_id, script_params,
+                            script_content):
+    """Creates a Python script `script_id` in the given `container`, with
+    `script_params` and `script_content`.
+
+    If the container already contains an object with id `script_id`, this
+    object is removed first.
+    """
+    if script_id in container.objectIds():
+       container.manage_delObjects([script_id])
+
+    container.manage_addProduct['PythonScripts']\
+                  .manage_addPythonScript(id = script_id)
+    script = container._getOb(script_id)
+    script.ZPythonScript_edit(script_params, script_content)
+    container.portal_url.getPortalObject().changeSkin(None)
+    return script
diff --git a/product/ERP5OOo/Document/OOoDocument.py b/product/ERP5OOo/Document/OOoDocument.py
index a0e06c3162d3ef13c8f313d943dcae8cd3be9e19..795c83219eae2d49d263d65fc8445f5e2ea8447a 100644
--- a/product/ERP5OOo/Document/OOoDocument.py
+++ b/product/ERP5OOo/Document/OOoDocument.py
@@ -43,7 +43,7 @@ from Products.ERP5Type.Utils import fill_args_from_request
 # Mixin Import
 from Products.ERP5.mixin.base_convertable import BaseConvertableFileMixin
 from Products.ERP5.mixin.text_convertable import TextConvertableMixin
-from Products.ERP5.mixin.extensible_traversable import OOoDocumentExtensibleTraversableMixin
+from Products.ERP5.mixin.ooo_document_extensible_traversable import OOoDocumentExtensibleTraversableMixin
 
 EMBEDDED_FORMAT = '_embedded'