[Erp5-report] r11916 - in /erp5/trunk/products/ERP5: Document/ PropertySheet/
nobody at svn.erp5.org
nobody at svn.erp5.org
Mon Jan 8 10:40:58 CET 2007
Author: jp
Date: Mon Jan 8 10:40:55 2007
New Revision: 11916
URL: http://svn.erp5.org?rev=11916&view=rev
Log:
Refactored ERP5 Web classes with abstract API.
Modified:
erp5/trunk/products/ERP5/Document/WebSection.py
erp5/trunk/products/ERP5/Document/WebSite.py
erp5/trunk/products/ERP5/PropertySheet/WebSite.py
Modified: erp5/trunk/products/ERP5/Document/WebSection.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/WebSection.py?rev=11916&r1=11915&r2=11916&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/WebSection.py (original)
+++ erp5/trunk/products/ERP5/Document/WebSection.py Mon Jan 8 10:40:55 2007
@@ -38,14 +38,64 @@
from zLOG import LOG
-from Products.ERP5.Document.WebSite import reserved_name_dict, reserved_name_dict_init
-from Products.ERP5.Document.WebSite import CACHE_KEY, WEBSITE_USER, WEBSECTION_KEY, DOCUMENT_NAME_KEY
-
+from Products.ERP5Type.Cache import getReadOnlyTransactionCache
+
+# Global keys used for URL generation
+WEBSECTION_KEY = 'web_section_value'
+WEBSITE_USER = 'web_site_user'
+
+Domain_getattr = Domain.inheritedAttribute('__getattr__')
+
+# We use a request key (CACHE_KEY) to store access attributes and prevent infinite recursion
+# We define a couple of reserved names for which we are not
+# going to try to do acquisition
+CACHE_KEY = 'web_site_aq_cache'
+DOCUMENT_NAME_KEY = 'web_section_document_name'
+reserved_name_dict = { 'getApplicableLayout' : 1,
+ 'getLayout' : 1,
+ 'Localizer' : 1,
+ 'field_render' : 1,
+ 'getListItemUrl' : 1,
+ 'getLocalPropertyManager' : 1,
+ 'getOrderedGlobalActionList' : 1,
+ 'allow_discussion' : 1,
+ 'im_func' : 1,
+ 'id' : 1,
+ 'method_id' : 1,
+ 'role_map' : 1,
+ 'func_defaults': 1, }
+reserved_name_dict_init = 0
class WebSection(Domain):
"""
A Web Section is a Domain with an extended API intended to
- support the creation of Web front ends to ERP5 contents.
+ support the creation of Web front ends to
+ server ERP5 contents through a pretty and configurable
+ user interface.
+
+ WebSection uses the following scripts for customisation:
+
+ - WebSection_getBreadcrumbItemList
+
+ - WebSection_getDocumentValueList
+
+ - WebSection_getPermanentURL
+
+ - WebSection_getDocumentValue
+
+ - WebSection_getDefaultDocumentValue
+
+ - WebSection_getSectionValue
+
+ - WebSection_getWebSiteValue
+
+ It defines the following REQUEST global variables:
+
+ - current_web_section
+
+ - current_web_document
+
+ - is_web_section_default_document
"""
# CMF Type Definition
meta_type = 'ERP5 Web Section'
@@ -66,31 +116,20 @@
, PropertySheet.SortIndex
)
- # Draft - this is being worked on
- # Due to some issues in acquisition, the implementation of getWebSectionValue
- # through acquisition by containment does not work for URLs
- # such as web_site_module/a/b/c/web_page_module/d
- # getWebSectionValue will return web_site_module/a/b instead
- # of web_site_module/a/b/c
- #security.declareProtected(Permissions.AccessContentsInformation, 'getWebSectionValue')
- #def getWebSectionValue(self):
- #"""
- #Returns the current web section (ie. self) though containment acquisition
- #"""
- #return self
+ web_section_key = WEBSECTION_KEY
def _aq_dynamic(self, name):
"""
Try to find a suitable document based on the
web site local naming policies as defined by
- the WebSite_getDocumentValue script
+ the getDocumentValue method
"""
global reserved_name_dict_init
global reserved_name_dict
request = self.REQUEST
# Register current web site physical path for later URL generation
- if not request.has_key(WEBSECTION_KEY):
- request[WEBSECTION_KEY] = self.getPhysicalPath()
+ if not request.has_key(self.web_section_key):
+ request[self.web_section_key] = self.getPhysicalPath()
# Normalize web parameter in the request
# Fix common user mistake and transform '1' string to boolean
for web_param in ['ignore_layout', 'editable_mode']:
@@ -105,6 +144,7 @@
return dynamic
# Do some optimisation here for names which can not be names of documents
if reserved_name_dict.has_key(name) \
+ or name.endswith('_getDocumentValue') \
or name.startswith('_') or name.startswith('portal_')\
or name.startswith('aq_') or name.startswith('selection_') \
or name.startswith('sort-') or name.startswith('WebSite_') \
@@ -135,7 +175,8 @@
if user is not None:
old_manager = getSecurityManager()
newSecurityManager(get_request(), user)
- document = self.WebSite_getDocumentValue(name=name, portal=portal)
+ LOG('Lookup', 0, str(name))
+ document = self.getDocumentValue(name=name, portal=portal)
request[CACHE_KEY][name] = document
if user is not None:
setSecurityManager(old_manager)
@@ -152,6 +193,198 @@
editable_absolute_url=document.absolute_url()))
return document
- security.declareProtected(Permissions.AccessContentsInformation, 'getWebSiteValue')
- def getWebSiteValue(self):
- return self.getParentValue().getWebSiteValue()
+ security.declareProtected(Permissions.AccessContentsInformation, 'getWebSectionValue')
+ def getWebSectionValue(self):
+ """
+ Returns the current web section (ie. self) though containment acquisition.
+
+ To understand the misteries of acquisition and how the rule
+ containment vs. acquisition works, please look at
+ XXXX (Zope web site)
+ """
+ return self
+
+ # Default view display
+ security.declareProtected(Permissions.View, '__call__')
+ def __call__(self):
+ """
+ If a Web Section has a default document, we render
+ the default document instead of rendering the Web Section
+ itself.
+
+ The implementation is based on the presence of specific
+ variables in the REQUEST (besides editable_mode and
+ ignore_layout).
+
+ current_web_section -- defines the Web Section which is
+ used to display the current document.
+
+ current_web_document -- defines the Document (ex. Web Page)
+ which is being displayed within current_web_section.
+
+ is_web_section_default_document -- a boolean which is
+ set each time we display a default document as a section.
+
+ We use REQUEST parameters so that they are reset for every
+ Web transaction and can be accessed from widgets.
+ """
+ self.REQUEST.set('current_web_section', self)
+ if not self.REQUEST.get('editable_mode') and not self.REQUEST.get('ignore_layout'):
+ document = self.getDefaultDocumentValue()
+ if document is not None:
+ self.REQUEST.set('current_web_document', document)
+ self.REQUEST.set('is_web_section_default_document', 1)
+ return document.__of__(self)()
+ return Domain.__call__(self)
+
+ # Layout Selection API
+ security.declareProtected(Permissions.AccessContentsInformation, 'getApplicableLayout')
+ def getApplicableLayout(self):
+ """
+ The applicable layout on a section is the container layout.
+ """
+ return self.getContainerLayout()
+
+ # WebSection API
+ security.declareProtected(Permissions.View, 'getDocumentValue')
+ def getDocumentValue(self, name=None, portal=None):
+ """
+ Return the default document with the given
+ name. The name parameter may represent anything
+ such as a document reference, an identifier,
+ etc.
+
+ If name is not provided, the method defaults
+ to returning the default document by calling
+ getDefaultDocumentValue.
+
+ This method must be implemented through a
+ portal type dependent script:
+ WebSection_getDocumentValue
+ """
+ if name is None:
+ return self.getDefaultDocumentValue()
+
+ cache = getReadOnlyTransactionCache(self)
+ method = None
+ if cache is not None:
+ key = ('getDocumentValue', self)
+ try:
+ method = cache[key]
+ except KeyError:
+ pass
+
+ if method is None: method = self._getTypeBasedMethod('getDocumentValue',
+ fallback_script_id='WebSection_getDocumentValue')
+
+ if cache is not None:
+ if not cache.has_key(key): cache[key] = method
+
+ return method(name, portal=portal)
+
+ security.declareProtected(Permissions.View, 'getDefaultDocumentValue')
+ def getDefaultDocumentValue(self):
+ """
+ Return the default document of the current
+ section.
+
+ This method must be implemented through a
+ portal type dependent script:
+ WebSection_getDefaultDocumentValue
+ """
+ cache = getReadOnlyTransactionCache(self)
+ if cache is not None:
+ key = ('getDefaultDocumentValue', self)
+ try:
+ return cache[key]
+ except KeyError:
+ pass
+
+ result = self._getTypeBasedMethod('getDefaultDocumentValue',
+ fallback_script_id='WebSection_getDefaultDocumentValue')()
+
+ if cache is not None:
+ cache[key] = result
+
+ return result
+
+ security.declareProtected(Permissions.View, 'getDocumentValueList')
+ def getDocumentValueList(self, **kw):
+ """
+ Return the list of documents which belong to the
+ current section. The API is designed to
+ support additional parameters so that it is possible
+ to group documents by reference, version, language, etc.
+ or to implement filtering of documents.
+
+ This method must be implemented through a
+ portal type dependent script:
+ WebSection_getDocumentValueList
+ """
+ cache = getReadOnlyTransactionCache(self)
+ if cache is not None:
+ key = ('getDocumentValueList', self) + tuple(kw.items())
+ try:
+ return cache[key]
+ except KeyError:
+ pass
+
+ result = self._getTypeBasedMethod('getDocumentValueList',
+ fallback_script_id='WebSection_getDocumentValueList')(**kw)
+
+ if cache is not None:
+ cache[key] = result
+
+ return result
+
+ security.declareProtected(Permissions.View, 'getPermanentURL')
+ def getPermanentURL(self, document):
+ """
+ Return a permanent URL of document in the context
+ of the current section.
+
+ This method must be implemented through a
+ portal type dependent script:
+ WebSection_getPermanentURL
+ """
+ cache = getReadOnlyTransactionCache(self)
+ if cache is not None:
+ key = ('getDocumentValueList', self, document.getPath())
+ try:
+ return cache[key]
+ except KeyError:
+ pass
+
+ result = self._getTypeBasedMethod('getPermanentURL',
+ fallback_script_id='WebSection_getPermanentURL')(document)
+
+ if cache is not None:
+ cache[key] = result
+
+ return result
+
+ security.declareProtected(Permissions.View, 'getBreadcrumbItemList')
+ def getBreadcrumbItemList(self, document):
+ """
+ Return a section dependent breadcrumb in the form
+ of a list of (title, document) tuples.
+
+ This method must be implemented through a
+ portal type dependent script:
+ WebSection_getBreadcrumbItemList
+ """
+ cache = getReadOnlyTransactionCache(self)
+ if cache is not None:
+ key = ('getDocumentValueList', self, document.getPath())
+ try:
+ return cache[key]
+ except KeyError:
+ pass
+
+ result = self._getTypeBasedMethod('getBreadcrumbItemList',
+ fallback_script_id='WebSection_getBreadcrumbItemList')(document)
+
+ if cache is not None:
+ cache[key] = result
+
+ return result
Modified: erp5/trunk/products/ERP5/Document/WebSite.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/WebSite.py?rev=11916&r1=11915&r2=11916&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/WebSite.py (original)
+++ erp5/trunk/products/ERP5/Document/WebSite.py Mon Jan 8 10:40:55 2007
@@ -26,29 +26,18 @@
##############################################################################
from Acquisition import ImplicitAcquisitionWrapper, aq_base, aq_inner
+from AccessControl import ClassSecurityInfo
-from AccessControl import ClassSecurityInfo
-from AccessControl.User import emergency_user
-from AccessControl.SecurityManagement import getSecurityManager, newSecurityManager, setSecurityManager
-
-from Products.CMFCore.utils import getToolByName
-from Products.ERP5.Document.Domain import Domain
+from Products.ERP5.Document.WebSection import WebSection, WEBSECTION_KEY
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface, Cache
-from Products.ERP5Type.Base import TempBase
-
-from Products.CMFCore.utils import UniqueObject, _checkPermission, _getAuthenticatedUser
from Globals import get_request
-
from Persistence import Persistent
-
from ZPublisher import BeforeTraverse
from zLOG import LOG
WEBSITE_KEY = 'web_site_value'
-WEBSECTION_KEY = 'web_section_value'
-WEBSITE_USER = 'web_site_user'
class WebSiteTraversalHook(Persistent):
"""
@@ -111,62 +100,10 @@
self._v_request = request
request.physicalPathToVirtualPath = self._physicalPathToVirtualPath
-
-
-Domain_getattr = Domain.inheritedAttribute('__getattr__')
-
-# Use a request key to store access attributes and prevent infinite recursion
-CACHE_KEY = 'web_site_aq_cache'
-DOCUMENT_NAME_KEY = 'web_site_document_name'
-reserved_name_dict = { 'getApplicableLayout' : 1,
- 'getLayout' : 1,
- 'Localizer' : 1,
- 'field_render' : 1,
- 'getListItemUrl' : 1,
- 'getLocalPropertyManager' : 1,
- 'getOrderedGlobalActionList' : 1,
- 'allow_discussion' : 1,
- 'im_func' : 1,
- 'id' : 1,
- 'method_id' : 1,
- 'role_map' : 1, }
-reserved_name_dict_init = 0
-
-class WebSite(Domain):
+class WebSite(WebSection):
"""
- A Web Site root class. This class is used by ERP5 Commerce
- to define the root of an eCommerce site.
-
- The main idea of the WebSite class is to provide access to
- documents by leveraging aq_dynamic with a user definable
- script: WebSite_getDocumentValue
-
- This script allows for implementing simple or
- complex document lookup policies:
-
- - access to documents by a unique reference (ex. as
- in a Wiki)
-
- - access to published documents only (ex.
- publication_state == 'published')
-
- - access to most relevant document (ex. latest
- version, applicable language)
-
- Changing this script allows for configuring completely
- the behaviour of a web site and tweaking document
- lookup policies to fit specific needs.
-
- WARNING:
- - Z Catalog Search permission must be set for Anonymous
- (XXX is this still true ?)
-
- TODO:
- - accelerate document lookup by caching acceptable keys
- (XXX is this still true ?)
-
- - fix missing REQUEST information in aq_dynamic documents
- (XXX is this still true ?)
+ The Web Site root class is specialises WebSection
+ by defining a global webmaster user.
"""
# CMF Type Definition
meta_type = 'ERP5 Web Site'
@@ -186,79 +123,8 @@
, PropertySheet.WebSite
)
- def _aq_dynamic(self, name):
- """
- Try to find a suitable document based on the
- web site local naming policies as defined by
- the WebSite_getDocumentValue script
- """
- global reserved_name_dict_init
- global reserved_name_dict
- request = self.REQUEST
- # Register current web site physical path for later URL generation
- if not request.has_key(WEBSITE_KEY):
- request[WEBSITE_KEY] = self.getPhysicalPath()
- # Normalize web parameter in the request
- # Fix common user mistake and transform '1' string to boolean
- for web_param in ['ignore_layout', 'editable_mode']:
- if hasattr(request, web_param):
- if getattr(request, web_param, None) in ('1', 1, True):
- request.set(web_param, True)
- else:
- request.set(web_param, False)
- # First let us call the super method
- dynamic = Domain._aq_dynamic(self, name)
- if dynamic is not None:
- return dynamic
- # Do some optimisation here for names which can not be names of documents
- if reserved_name_dict.has_key(name) \
- or name.startswith('_') or name.startswith('portal_')\
- or name.startswith('aq_') or name.startswith('selection_') \
- or name.startswith('sort-') or name.startswith('WebSite_') \
- or name.startswith('WebSection_') or name.startswith('Base_'):
- return None
- if not reserved_name_dict_init:
- # Feed reserved_name_dict_init with skin names
- portal = self.getPortalObject()
- for skin_folder in portal.portal_skins.objectValues():
- for id in skin_folder.objectIds():
- reserved_name_dict[id] = 1
- for id in portal.objectIds():
- reserved_name_dict[id] = 1
- reserved_name_dict_init = 1
- #LOG('aq_dynamic name',0, name)
- if not request.has_key(CACHE_KEY):
- request[CACHE_KEY] = {}
- elif request[CACHE_KEY].has_key(name):
- return request[CACHE_KEY][name]
- try:
- portal = self.getPortalObject()
- # Use the webmaster identity to find documents
- if request[CACHE_KEY].has_key(WEBSITE_USER):
- user = request[CACHE_KEY][WEBSITE_USER] # Retrieve user from request cache
- else:
- user = portal.acl_users.getUserById(self.getWebmaster())
- request[CACHE_KEY][WEBSITE_USER] = user # Cache user per request
- if user is not None:
- old_manager = getSecurityManager()
- newSecurityManager(get_request(), user)
- document = self.WebSite_getDocumentValue(name=name, portal=portal)
- request[CACHE_KEY][name] = document
- if user is not None:
- setSecurityManager(old_manager)
- except:
- # Cleanup non recursion dict in case of exception
- if request[CACHE_KEY].has_key(name):
- del request[CACHE_KEY][name]
- raise
- if document is not None:
- document = aq_base(document.asContext(id=name, # Hide some properties to permit location the original
- original_container=document.getParentValue(),
- original_id=document.getId(),
- editable_absolute_url=document.absolute_url()))
- return document
+ web_section_key = WEBSITE_KEY
- # Draft - this is being worked on
security.declareProtected(Permissions.AccessContentsInformation, 'getWebSiteValue')
def getWebSiteValue(self):
"""
@@ -266,24 +132,29 @@
"""
return self
+ # Virtual Hosting Support
security.declarePrivate( 'manage_beforeDelete' )
def manage_beforeDelete(self, item, container):
if item is self:
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.unregisterBeforeTraverse(item, handle)
- Domain.manage_beforeDelete(self, item, container)
+ WebSection.manage_beforeDelete(self, item, container)
security.declarePrivate( 'manage_afterAdd' )
def manage_afterAdd(self, item, container):
if item is self:
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.registerBeforeTraverse(item, WebSiteTraversalHook(), handle)
- Domain.manage_afterAdd(self, item, container)
+ WebSection.manage_afterAdd(self, item, container)
# Experimental methods
- def findUrlList(self, document):
- """
- Return a list of URLs which exist in the site for
- a given document
- """
- pass
+ def getPermanentURLList(self, document):
+ """
+ Return a list of URLs which exist in the site for
+ a given document. This could be implemented either
+ by keep a history of documents which have been
+ accessed or by parsing all WebSections and listing
+ all documents in each of them to build a reverse
+ mapping of getPermanentURL
+ """
+ pass
Modified: erp5/trunk/products/ERP5/PropertySheet/WebSite.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/PropertySheet/WebSite.py?rev=11916&r1=11915&r2=11916&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/PropertySheet/WebSite.py (original)
+++ erp5/trunk/products/ERP5/PropertySheet/WebSite.py Mon Jan 8 10:40:55 2007
@@ -34,10 +34,24 @@
{ 'id' : 'container_layout',
'description' : 'ID of a page template or form which defines the rendering layout for the container',
'type' : 'string',
+ 'default' : None,
+ 'acquisition_base_category' : ('parent',),
+ 'acquisition_portal_type' : ('Web Section', 'Web Site'),
+ 'acquisition_copy_value' : 0,
+ 'acquisition_mask_value' : 1,
+ 'acquisition_accessor_id' : 'getContainerLayout',
+ 'acquisition_depends' : None,
'mode' : '' },
{ 'id' : 'content_layout',
'description' : 'ID of a page template or form which defines the rendering layout for contents',
'type' : 'string',
+ 'default' : None,
+ 'acquisition_base_category' : ('parent',),
+ 'acquisition_portal_type' : ('Web Section', 'Web Site'),
+ 'acquisition_copy_value' : 0,
+ 'acquisition_mask_value' : 1,
+ 'acquisition_accessor_id' : 'getContentLayout',
+ 'acquisition_depends' : None,
'mode' : '' },
{ 'id' : 'webmaster',
'description' : 'ID of a user which has complete access to all documents in the site.',
More information about the Erp5-report
mailing list