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'