Commit 8e211e3e authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: erp5_web: Migrate Documents and Unit Tests (MR !1219).

* Properly override WebSite Document in erp5_web_shadir by creating a new
  version_priority.
* Keep Web{Section,Site}TraversalHook in Product/ERP5/Document as these are
  not ERP5 objects. Also, add `kept_for_backward_compatibility_only` to
  importLocalDocument() to handle pattern where the Document class has been
  migrated but some code remains.
* Move WebSection_get*PrecacheManifestList Python Script from bt5 not depending
  on erp5_web to erp5_web_renderjs as this is required by CodingStyleTest
  (WebSection being now in erp5_web).
parent e57dd4fb
......@@ -2,10 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<tuple>
<global name="WebSite" module="Products.ERP5.Document.WebSite"/>
<tuple/>
</tuple>
<global name="Web Site" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -244,10 +241,7 @@
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="WebSiteTraversalHook" module="Products.ERP5.Document.WebSite"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary/>
......
erp5_web
\ No newline at end of file
......@@ -70,7 +70,6 @@ class TestERP5WebWithCRM(ERP5TypeTestCase):
"""
Setup Web Site
"""
portal = self.getPortal()
website = self.getPortal().web_site_module.newContent(portal_type='Web Site',
**kw)
websection = website.newContent(portal_type='Web Section', **kw)
......@@ -145,7 +144,6 @@ class TestERP5WebWithCRM(ERP5TypeTestCase):
web_section.WebSection_addWebMessage(**form_kw)
transaction.commit()
# here we check a random bug caused by the ordering of activities
should_stop = [None]
event_module_path_prefix = self.portal.event_module.getPath() + '/'
deprioritize_message_list = []
# we'll stop whenever we find the message that reindex the newly created
......@@ -159,7 +157,8 @@ class TestERP5WebWithCRM(ERP5TypeTestCase):
return True
return False
self.tic(stop_condition=stop_condition)
web_message_reindex_message, = deprioritize_message_list
assert len(deprioritize_message_list) == 1
web_message_reindex_message = deprioritize_message_list[0]
web_message_path = web_message_reindex_message.object_path
self.assertTrue(
self.portal.unrestrictedTraverse(web_message_path).getPortalType(),
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testERP5WebWithCRM</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.tests.testERP5WebWithCRM</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testERP5WebWithCRM</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
test.erp5.testCRMNotificationTool
test.erp5.testCRMSupportRequest
test.erp5.testERP5WebWithCRM
\ No newline at end of file
erp5_full_text_mroonga_catalog
erp5_core_test
erp5_web
\ No newline at end of file
......@@ -32,7 +32,7 @@ from Acquisition import aq_base
from OFS.Traversable import NotFound
from Products.ERP5.mixin.document_extensible_traversable import DocumentExtensibleTraversableMixin
from Products.ERP5.Document.WebSection import WebSection
from erp5.component.document.WebSection import WebSection
from Products.ERP5Type import Permissions
from webdav.NullResource import NullResource
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2002-2008 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 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 import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.Domain import Domain
from Products.ERP5.Document.WebSection import WebSectionTraversalHook
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
from OFS.Traversable import NotFound
from ZPublisher import BeforeTraverse
from Products.CMFCore.utils import _checkConditionalGET, _setCacheHeaders, _ViewEmulator
from Products.ERP5Type.Cache import getReadOnlyTransactionCache
# Global keys used for URL generation
WEBSECTION_KEY = 'web_section_value'
MARKER = []
WEB_SECTION_PORTAL_TYPE_TUPLE = ('Web Section', 'Web Site')
class WebSection(Domain, DocumentExtensibleTraversableMixin):
"""
A Web Section is a Domain with an extended API intended to
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_getWebSectionValue
- 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'
portal_type = 'Web Section'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.WebSection
, PropertySheet.SortIndex
, PropertySheet.Predicate
)
web_section_key = WEBSECTION_KEY
security.declareProtected(Permissions.View, '__bobo_traverse__')
def __bobo_traverse__(self, request, name):
"""
If no subobject is found through Folder API
then try to lookup the object by invoking getDocumentValue
"""
# Register current web site physical path for later URL generation
if request.get(self.web_section_key, MARKER) is MARKER:
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']:
if hasattr(request, web_param):
param = getattr(request, web_param, None)
if isinstance(param, (list, tuple)):
param = param[0]
if param in ('1', 1, True):
request.set(web_param, True)
else:
request.set(web_param, False)
document = None
try:
document = DocumentExtensibleTraversableMixin.__bobo_traverse__(self, request, name)
except NotFound:
not_found_page_ref = self.getLayoutProperty('layout_not_found_page_reference')
if not_found_page_ref:
document = DocumentExtensibleTraversableMixin.getDocumentValue(self, name=not_found_page_ref)
if document is None:
# if no document found, fallback on default page template
document = DocumentExtensibleTraversableMixin.__bobo_traverse__(self, request,
'404.error.page')
return document
security.declarePrivate( 'manage_beforeDelete' )
def manage_beforeDelete(self, item, container):
if item is self:
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.unregisterBeforeTraverse(item, handle)
super(WebSection, self).manage_beforeDelete(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, self._getTraversalHookClass()(), handle)
super(WebSection, self).manage_afterAdd(item, container)
security.declarePrivate( 'manage_afterClone' )
def manage_afterClone(self, item):
self._cleanupBeforeTraverseHooks()
super(WebSection, self).manage_afterClone(item)
def _getTraversalHookClass(self):
return WebSectionTraversalHook
_traversal_hook_class = WebSectionTraversalHook
def _cleanupBeforeTraverseHooks(self):
# unregister all before traversal hooks that do not belong to us.
my_handle = self.meta_type + '/' + self.getId()
handle_to_unregister_list = []
for (_, handle), hook in self.__before_traverse__.items():
if isinstance(hook, self._getTraversalHookClass()) and handle != my_handle:
handle_to_unregister_list.append(handle)
for handle in handle_to_unregister_list:
BeforeTraverse.unregisterBeforeTraverse(self, handle)
security.declareProtected(Permissions.AccessContentsInformation, 'getLayoutProperty')
def getLayoutProperty(self, key, default=None):
"""
A simple method to get a property of the current by
acquiring it from the current section or its parents.
"""
section = aq_inner(self)
while section.getPortalType() in ('Web Section', 'Web Site', 'Static Web Section', 'Static Web Site'):
result = section.getProperty(key, MARKER)
if result not in (MARKER, None):
return result
section = section.aq_parent
return default
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.
"""
# Register current web site physical path for later URL generation
if self.REQUEST.get(self.web_section_key, MARKER) is MARKER:
self.REQUEST[self.web_section_key] = self.getPhysicalPath()
self.REQUEST.set('current_web_section', self)
if not self.REQUEST.get('editable_mode') and not self.REQUEST.get('ignore_layout'):
document = None
if self.isDefaultPageDisplayed():
# The following could be moved to a typed based method for more flexibility
document = self.getDefaultDocumentValue()
if document is None:
# 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.getDefaultDocumentValue) is not None:
# force user to login as specified in Web Section
raise Unauthorized
if document is not None and document.getReference() is not None:
# we use web_site_module/site_id/section_id/page_reference
# as the url of the default document.
self.REQUEST.set('current_web_document', document)
self.REQUEST.set('is_web_section_default_document', 1)
document = aq_base(document.asContext(
id=document.getReference(),
original_container=document.getParentValue(),
original_id=document.getId(),
editable_absolute_url=document.absolute_url())).__of__(self)
else:
isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
if isAuthorizationForced is not None and isAuthorizationForced():
if self.getPortalObject().portal_membership.isAnonymousUser():
# force anonymous to login
raise Unauthorized
# Try to use a custom renderer if any
custom_render_method_id = self.getCustomRenderMethodId()
if custom_render_method_id is not None:
if document is None:
document = self
result = getattr(document, custom_render_method_id)()
view = _ViewEmulator().__of__(self)
# If we have a conditional get, set status 304 and return
# no content
if _checkConditionalGET(view, extra_context={}):
return ''
# call caching policy manager.
_setCacheHeaders(view, {})
return result
elif document is not None:
return document()
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, '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()
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
if result is not None:
result = result.__of__(self)
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()
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
if result is not None and not kw.get('src__', 0):
result = [doc.__of__(self) for doc in result]
return result
security.declareProtected(Permissions.View, 'getPermanentURL')
def getPermanentURL(self, document, view=True):
"""
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
XXX The following argument is obsoleted because we no longer need /view.
If view is True, the url returned point to html content and can be
opened in a browser (ie. + '/view' for ooo documents)
"""
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getPermanentURL', self, document.getPath())
try:
return cache[key]
except KeyError:
pass
document = document.getObject().__of__(self)
result = document._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=None):
"""
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
"""
if document is None:
document = self
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getBreadcrumbItemList', 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
security.declareProtected(Permissions.View, 'getSiteMapTree')
def getSiteMapTree(self, **kw):
"""
Return a site map tree section dependent breadcrumb in the
form of a list of dicts whose structure is provided as a tree
so that it is easy to implement recursive call with TAL/METAL:
[
{
'url' : '/erp5/web_site_module/site/section',
'level' : 1,
'translated_title' : 'Section Title',
'subsection' : [
{
'url' : '/erp5/web_site_module/site/section/reference',
'level' : 2,
'translated_title' : 'Sub Section Title',
'subsection' : None,
},
...
],
}
...
]
This method must be implemented through a
portal type dependent script:
WebSection_getSiteMapTree
"""
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getSiteMapTree', self) + tuple(kw.items())
try:
return cache[key]
except KeyError:
pass
result = self._getTypeBasedMethod('getSiteMapTree',
fallback_script_id='WebSection_getSiteMapTree')(**kw)
if cache is not None:
cache[key] = result
return result
def _edit(self, **kw):
# XXX it is unclear if we should keep this behavior in other potential subclasses.
# Probably yes.
if self.getPortalType() in WEB_SECTION_PORTAL_TYPE_TUPLE:
if getattr(self, '__before_traverse__', None) is None:
# migrate beforeTraverse hook if missing
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.registerBeforeTraverse(self, self._getTraversalHookClass()(), handle)
else:
# cleanup beforeTraverse hooks that may exist after this document was cloned.
self._cleanupBeforeTraverseHooks()
super(WebSection, self)._edit(**kw)
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>WebSection</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.WebSection</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.WebSection</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
#
# Copyright (c) 2002-2006 Nexedi SARL and Contributors. All Rights Reserved.
#
# 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 import ClassSecurityInfo
from erp5.component.document.WebSection import WebSection
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Cache import CachingMethod
from warnings import warn
from zExceptions import Redirect
WEBSITE_KEY = 'web_site_value'
WEBSITE_LANGUAGE_KEY = 'web_site_language'
class WebSite(WebSection):
"""
The Web Site root class is specialises WebSection
by defining a global webmaster user.
"""
# CMF Type Definition
meta_type = 'ERP5 Web Site'
portal_type = 'Web Site'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.WebSection
, PropertySheet.WebSite
, PropertySheet.Predicate
)
web_section_key = WEBSITE_KEY
security.declareProtected(Permissions.AccessContentsInformation, 'getWebSiteValue')
def getWebSiteValue(self):
"""
Returns the current web site (ie. self) though containment acquisition
"""
return self
# Static Language Selection support
def getExtensibleContent(self, request, name):
language_list = self.getAvailableLanguageList()
if language_list and self.isStaticLanguageSelection():
# Interprete names which could be a language
# as a language selection only if language_list
# was defined or set default language
if name in language_list:
default_language = self.getDefaultAvailableLanguage()
if request.get('AcceptLanguage') is not None:
request['AcceptLanguage'].set(name, 100)
request.set(WEBSITE_LANGUAGE_KEY, name)
if self.isTempObject() or name == default_language:
redirect_path_list = [self.getOriginalDocument().absolute_url()]
if name != default_language:
redirect_path_list.append(name)
redirect_path_list.extend(reversed(request['TraversalRequestNameStack']))
request['minimum_language_redirect_url'] = '/'.join(redirect_path_list)
query_string = request.get('QUERY_STRING')
if query_string:
request['minimum_language_redirect_url'] += '?' + query_string
return self.getOriginalDocument().asContext(id=name, __language_web_site=True)
return WebSection.getExtensibleContent(self, request, name)
security.declarePublic('isSubtreeIndexable')
def isSubtreeIndexable(self):
if self.isTempObject() and getattr(self, '__language_web_site', False):
# temp Web Site used to select a language must not prevent
# document indexation
return self.aq_inner.aq_parent.isSubtreeIndexable()
return super(WebSite, self).isSubtreeIndexable()
def _getExtensibleContent(self, request, name):
"""
Legacy API
"""
warn("_getExtensibleContent() function is deprecated. Use getExtensibleContent() instead.", \
DeprecationWarning, stacklevel=2)
return self.getExtensibleContent(request, name)
def _getTraversalHookClass(self):
from Products.ERP5.Document.WebSite import WebSiteTraversalHook
return WebSiteTraversalHook
def __before_publishing_traverse__(self, self2, request):
redirect_url = request.get('minimum_language_redirect_url')
if redirect_url:
raise Redirect(redirect_url)
return super(WebSite, self).__before_publishing_traverse__(self2, request)
security.declareProtected(Permissions.AccessContentsInformation, 'getPermanentURLList')
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
"""
return [x.getPermanentURL(document) for x in self.getWebSectionValueList(document)]
security.declareProtected(Permissions.AccessContentsInformation, 'getWebSectionValueList')
def getWebSectionValueList(self, document):
"""
Returns a list of sections which a given document is
part of.
This could be implemented either by testing all sections
and building a cache or by using the predicate API
to find which sections apply.
"""
def getWebSectionUidList(section):
# Only return visible web section
if section.isVisible():
result = [section.getUid()]
else:
result = []
for o in section.contentValues(portal_type='Web Section'):
result.extend(getWebSectionUidList(o))
return result
_getWebSectionUidList = CachingMethod(getWebSectionUidList,
id='WebSite._getWebSectionUidList',
cache_factory='erp5_content_medium')
web_section_uid_list = _getWebSectionUidList(self)
if web_section_uid_list:
section_list = self.portal_domains.searchPredicateList(document,
portal_type='Web Section',
uid=web_section_uid_list)
section_dict = {}
for section in section_list:
section_dict[section.getPhysicalPath()] = section
# Eliminate path
for section in section_list:
path = section.getPhysicalPath()
for i in range(0, len(path)):
sub_path = tuple(path[0:i])
if section_dict.has_key(sub_path):
del section_dict[sub_path]
section_list = section_dict.values()
# Sort by Index
section_list.sort(key=lambda x: x.getIntIndex())
return section_list
else:
return []
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>WebSite</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.WebSite</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.WebSite</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -109,7 +109,7 @@ class TestWebSiteTraversalHook(WebTraversalHookTestMixin, ERP5TypeTestCase):
self.web_section = self.portal.web_site_module.newContent(
portal_type='Web Site',
)
from Products.ERP5Type.Document.WebSite import WebSiteTraversalHook
from Products.ERP5.Document.WebSite import WebSiteTraversalHook
self.traversal_hook_class = WebSiteTraversalHook
......@@ -121,7 +121,7 @@ class TestWebSectionTraversalHook(WebTraversalHookTestMixin, ERP5TypeTestCase):
)
self.web_section = web_site.newContent(portal_type='Web Section')
from Products.ERP5Type.Document.WebSection import WebSectionTraversalHook
from erp5.component.document.WebSection import WebSectionTraversalHook
self.traversal_hook_class = WebSectionTraversalHook
......@@ -248,7 +248,7 @@ class TestERP5Web(ERP5TypeTestCase):
portal_catalog = self.getCatalogTool()
try:
portal_catalog.catalog_object(web_site)
except:
except Exception:
self.fail('Cataloging of the Web Site failed.')
......@@ -346,10 +346,10 @@ Hé Hé Hé!""", page.asText().strip())
current user selected language in browser.
"""
portal = self.getPortal()
website = self.setupWebSite()
self.setupWebSite()
websection = self.setupWebSection()
page_reference = 'default-webpage'
webpage_list = self.setupWebSitePages(prefix=page_reference)
self.setupWebSitePages(prefix=page_reference)
# set default web page for section
found_by_reference = portal.portal_catalog(reference=page_reference,
......@@ -420,7 +420,7 @@ Hé Hé Hé!""", page.asText().strip())
Note: due to generic ERP5 Web implementation this test highly depends
on WebSection_geDefaulttDocumentValueList
"""
website = self.setupWebSite()
self.setupWebSite()
websection = self.setupWebSection()
publication_section_category_id_list = ['documentation', 'administration']
......@@ -487,7 +487,7 @@ Hé Hé Hé!""", page.asText().strip())
""" Check getting getDocumentValueList from Web Section.
"""
portal = self.getPortal()
website = self.setupWebSite()
self.setupWebSite()
websection = self.setupWebSection()
publication_section_category_id_list = ['documentation', 'administration']
......@@ -717,7 +717,7 @@ Hé Hé Hé!""", page.asText().strip())
web_page_list = self.setupWebSitePages('test1',
language_list=('en',),
publication_section_list=('my_test_category',))
web_page_list2 = self.setupWebSitePages('test2',
self.setupWebSitePages('test2',
language_list=('en',),
publication_section_list=('my_test_category',))
......@@ -1091,10 +1091,9 @@ Hé Hé Hé!""", page.asText().strip())
request['PARENTS'] = [self.app]
website = self.setupWebSite()
web_section_portal_type = 'Web Section'
web_section = website.newContent(portal_type=web_section_portal_type)
website.newContent(portal_type=web_section_portal_type)
content = '<p>initial text</p>'
new_content = '<p>modified text<p>'
document = portal.web_page_module.newContent(portal_type='Web Page',
id='document_original_cache',
reference='NXD-Document.Cache',
......@@ -1234,7 +1233,7 @@ Hé Hé Hé!""", page.asText().strip())
self.login()
self.tic()
web_site = self.setupWebSite()
self.setupWebSite()
websection = self.setupWebSection()
websection_url = '%s/%s' % (self.portal.getId(), websection.getRelativeUrl())
......@@ -1263,7 +1262,7 @@ Hé Hé Hé!""", page.asText().strip())
"""
website = self.setupWebSite()
web_section_portal_type = 'Web Section'
web_section = website.newContent(portal_type=web_section_portal_type)
website.newContent(portal_type=web_section_portal_type)
content = '<p>initial text</p>'
document = self.portal.web_page_module.newContent(portal_type='Web Page',
......@@ -1320,7 +1319,6 @@ Hé Hé Hé!""", page.asText().strip())
request['PARENTS'] = [self.app]
website = self.setupWebSite()
path = website.absolute_url_path() + '/a_non_existing_page'
absolute_url = website.absolute_url() + '/a_non_existing_page'
request = portal.REQUEST
# Check a Not Found page is returned
......@@ -1475,10 +1473,7 @@ Hé Hé Hé!""", page.asText().strip())
"""
Test Web Site map script.
"""
request = self.app.REQUEST
website = self.setupWebSite()
kw = {'depth': 5,
'include_subsection': 1}
website.setSiteMapSectionParent(1)
websection1 = website.newContent(portal_type='Web Section',
......@@ -1601,7 +1596,7 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
def getTitle(self):
return "Web"
def createUser(self, name, role_list):
def createUser(self, name, role_list): # pylint: disable=arguments-differ
user_folder = self.getPortal().acl_users
user_folder._doAddUser(name, 'password', role_list, [])
......@@ -1875,7 +1870,7 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
# test for admin
try:
site_1 = web_site_module.newContent(portal_type='Web Site', id='site_1')
web_site_module.newContent(portal_type='Web Site', id='site_1')
except Unauthorized:
self.fail("Admin should be able to create a Web Site.")
......@@ -1927,7 +1922,7 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
# test for admin
try:
base_category_1 = portal_categories.newContent(portal_type='Base Category', id='base_category_1')
portal_categories.newContent(portal_type='Base Category', id='base_category_1')
except Unauthorized:
self.fail("Admin should be able to create a Base Category.")
try:
......@@ -1947,7 +1942,7 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
# test as a web user (assignor)
self.loginByUserName('webmaster')
try:
base_category_2 = portal_categories.newContent(portal_type='Base Category', id='base_category_2')
portal_categories.newContent(portal_type='Base Category', id='base_category_2')
self.fail("A webmaster should not be able to create a Base Category.")
except Unauthorized:
pass
......@@ -1981,7 +1976,7 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
# test for admin
try:
new_base_category_1 = portal_categories.newContent(portal_type='Base Category', id='new_base_category_1')
portal_categories.newContent(portal_type='Base Category', id='new_base_category_1')
except Unauthorized:
self.fail("Admin should be able to create a Base Category.")
try:
......@@ -1992,14 +1987,14 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
self.fail("Admin should be able to create a Category.")
self.tic()
try:
new_cat_1_renamed = new_category_1.edit(id='new_cat_1_renamed')
new_cat_2_renamed = new_category_2.edit(id='new_cat_2_renamed')
new_category_1.edit(id='new_cat_1_renamed')
new_category_2.edit(id='new_cat_2_renamed')
except Unauthorized:
self.fail("Admin should be able to rename a Category.")
# test as a web user (assignor)
self.loginByUserName('webmaster')
try:
base_category_2 = portal_categories.newContent(portal_type='Base Category', id='base_category_2')
portal_categories.newContent(portal_type='Base Category', id='base_category_2')
self.fail("A webmaster should not be able to create a Base Category.")
except Unauthorized:
pass
......@@ -2012,8 +2007,8 @@ class TestERP5WebWithSimpleSecurity(ERP5TypeTestCase):
self.fail("A webmaster should be able to create a Category.")
self.tic()
try:
new_cat_3_renamed = new_category_3.edit(id='new_cat_3_renamed')
new_cat_4_renamed = new_category_4.edit(id='new_cat_4_renamed')
new_category_3.edit(id='new_cat_3_renamed')
new_category_4.edit(id='new_cat_4_renamed')
except Unauthorized:
self.fail("A webmaster should be able to rename a Category.")
......@@ -2162,7 +2157,7 @@ class ERP5WebUpgraderMixin(object):
class TestERP5WebSiteUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
def afterSetUp(self):
super(ERP5WebUpgraderMixin, self).afterSetUp()
super(TestERP5WebSiteUpgrader, self).afterSetUp()
self.upgraded = self.portal.web_site_module.newContent(
portal_type='Web Site',
)
......@@ -2170,7 +2165,7 @@ class TestERP5WebSiteUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
class TestERP5WebSectionUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
def afterSetUp(self):
super(ERP5WebUpgraderMixin, self).afterSetUp()
super(TestERP5WebSectionUpgrader, self).afterSetUp()
self.upgraded = self.portal.web_site_module.newContent(
portal_type='Web Site',
).newContent(
......@@ -2179,7 +2174,7 @@ class TestERP5WebSectionUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
class TestERP5StaticWebSiteUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
def afterSetUp(self):
super(ERP5WebUpgraderMixin, self).afterSetUp()
super(TestERP5StaticWebSiteUpgrader, self).afterSetUp()
self.upgraded = self.portal.web_site_module.newContent(
portal_type='Static Web Site',
)
......@@ -2187,7 +2182,7 @@ class TestERP5StaticWebSiteUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
class TestERP5StaticWebSectionUpgrader(ERP5WebUpgraderMixin, ERP5TypeTestCase):
def afterSetUp(self):
super(ERP5WebUpgraderMixin, self).afterSetUp()
super(TestERP5StaticWebSectionUpgrader, self).afterSetUp()
self.upgraded = self.portal.web_site_module.newContent(
portal_type='Web Site',
).newContent(
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testERP5Web</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.tests.testERP5Web</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testERP5Web</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
document.erp5.StaticWebSection
document.erp5.StaticWebSite
document.erp5.WebSection
document.erp5.WebSite
\ No newline at end of file
test.erp5.testWebSiteLanguage
test.erp5.testERP5Web
test.erp5.testWebPageConvert
test.erp5.testWebSiteLanguage
\ No newline at end of file
......@@ -2,3 +2,4 @@ erp5_full_text_mroonga_catalog
erp5_ui_test_core
erp5_ui_test
erp5_l10n_fr
erp5_project
\ No newline at end of file
......@@ -27,7 +27,7 @@
#
##############################################################################
from Products.ERP5.Document.WebSite import WebSite as SimpleWebSite
from erp5.component.document.erp5_version.WebSite import WebSite as SimpleWebSite
from erp5.component.mixin.VirtualFolderMixin import VirtualFolderMixin
class WebSite(VirtualFolderMixin, SimpleWebSite):
......
......@@ -12,7 +12,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.WebSite</string> </value>
<value> <string>document.erp5_web_shadir.WebSite</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
......@@ -26,7 +26,7 @@
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
<value> <string>erp5_web_shadir</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
......
document.erp5.WebSite
\ No newline at end of file
document.erp5_web_shadir.WebSite
\ No newline at end of file
......@@ -6642,6 +6642,8 @@ Business Template is a set of definitions, such as skins, portal types and categ
'Products.ERP5Form.VideoField',
'Products.ERP5Form.ZGDChart',
'Products.ERP5Form.ZPyChart',
'Products.ERP5.Document.WebSection',
'Products.ERP5.Document.WebSite',
# ERP5 Objects use Interaction Workflows...
'Products.ERP5Form.Interactor.FieldValueCacheInteractor',
## No need to migrate
......
......@@ -27,25 +27,15 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.Domain import Domain
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
from OFS.Traversable import NotFound
from Persistence import Persistent
from ZPublisher import BeforeTraverse
from Products.CMFCore.utils import _checkConditionalGET, _setCacheHeaders, _ViewEmulator
from Products.ERP5Type.Cache import getReadOnlyTransactionCache
# Global keys used for URL generation
WEBSECTION_KEY = 'web_section_value'
MARKER = []
WEB_SECTION_PORTAL_TYPE_TUPLE = ('Web Section', 'Web Site')
# WebSection Document class has been migrated to ZODB Components and this module
# has been kept here because of WebSectionTraversalHook which is not an ERP5
# object
kept_for_backward_compatibility_only = True
class WebSectionTraversalHook(Persistent):
"""Traversal hook to change the skin selection for this websection.
"""
......@@ -57,409 +47,3 @@ class WebSectionTraversalHook(Persistent):
((request.get('portal_skin', None) is None) or \
container.getPortalType() not in WEB_SECTION_PORTAL_TYPE_TUPLE):
container.getPortalObject().changeSkin(skin_selection_name)
class WebSection(Domain, DocumentExtensibleTraversableMixin):
"""
A Web Section is a Domain with an extended API intended to
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_getWebSectionValue
- 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'
portal_type = 'Web Section'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.WebSection
, PropertySheet.SortIndex
, PropertySheet.Predicate
)
web_section_key = WEBSECTION_KEY
security.declareProtected(Permissions.View, '__bobo_traverse__')
def __bobo_traverse__(self, request, name):
"""
If no subobject is found through Folder API
then try to lookup the object by invoking getDocumentValue
"""
# Register current web site physical path for later URL generation
if request.get(self.web_section_key, MARKER) is MARKER:
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']:
if hasattr(request, web_param):
param = getattr(request, web_param, None)
if isinstance(param, (list, tuple)):
param = param[0]
if param in ('1', 1, True):
request.set(web_param, True)
else:
request.set(web_param, False)
document = None
try:
document = DocumentExtensibleTraversableMixin.__bobo_traverse__(self, request, name)
except NotFound:
not_found_page_ref = self.getLayoutProperty('layout_not_found_page_reference')
if not_found_page_ref:
document = DocumentExtensibleTraversableMixin.getDocumentValue(self, name=not_found_page_ref)
if document is None:
# if no document found, fallback on default page template
document = DocumentExtensibleTraversableMixin.__bobo_traverse__(self, request,
'404.error.page')
return document
security.declarePrivate( 'manage_beforeDelete' )
def manage_beforeDelete(self, item, container):
if item is self:
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.unregisterBeforeTraverse(item, handle)
super(WebSection, self).manage_beforeDelete(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, self._getTraversalHookClass()(), handle)
super(WebSection, self).manage_afterAdd(item, container)
security.declarePrivate( 'manage_afterClone' )
def manage_afterClone(self, item):
self._cleanupBeforeTraverseHooks()
super(WebSection, self).manage_afterClone(item)
def _getTraversalHookClass(self):
return WebSectionTraversalHook
_traversal_hook_class = WebSectionTraversalHook
def _cleanupBeforeTraverseHooks(self):
# unregister all before traversal hooks that do not belong to us.
my_handle = self.meta_type + '/' + self.getId()
handle_to_unregister_list = []
for (priority, handle), hook in self.__before_traverse__.items():
if isinstance(hook, self._getTraversalHookClass()) and handle != my_handle:
handle_to_unregister_list.append(handle)
for handle in handle_to_unregister_list:
BeforeTraverse.unregisterBeforeTraverse(self, handle)
security.declareProtected(Permissions.AccessContentsInformation, 'getLayoutProperty')
def getLayoutProperty(self, key, default=None):
"""
A simple method to get a property of the current by
acquiring it from the current section or its parents.
"""
section = aq_inner(self)
while section.getPortalType() in ('Web Section', 'Web Site', 'Static Web Section', 'Static Web Site'):
result = section.getProperty(key, MARKER)
if result not in (MARKER, None):
return result
section = section.aq_parent
return default
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.
"""
# Register current web site physical path for later URL generation
if self.REQUEST.get(self.web_section_key, MARKER) is MARKER:
self.REQUEST[self.web_section_key] = self.getPhysicalPath()
self.REQUEST.set('current_web_section', self)
if not self.REQUEST.get('editable_mode') and not self.REQUEST.get('ignore_layout'):
document = None
if self.isDefaultPageDisplayed():
# The following could be moved to a typed based method for more flexibility
document = self.getDefaultDocumentValue()
if document is None:
# 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.getDefaultDocumentValue) is not None:
# force user to login as specified in Web Section
raise Unauthorized
if document is not None and document.getReference() is not None:
# we use web_site_module/site_id/section_id/page_reference
# as the url of the default document.
self.REQUEST.set('current_web_document', document)
self.REQUEST.set('is_web_section_default_document', 1)
document = aq_base(document.asContext(
id=document.getReference(),
original_container=document.getParentValue(),
original_id=document.getId(),
editable_absolute_url=document.absolute_url())).__of__(self)
else:
isAuthorizationForced = getattr(self, 'isAuthorizationForced', None)
if isAuthorizationForced is not None and isAuthorizationForced():
if self.getPortalObject().portal_membership.isAnonymousUser():
# force anonymous to login
raise Unauthorized
# Try to use a custom renderer if any
custom_render_method_id = self.getCustomRenderMethodId()
if custom_render_method_id is not None:
if document is None:
document = self
result = getattr(document, custom_render_method_id)()
view = _ViewEmulator().__of__(self)
# If we have a conditional get, set status 304 and return
# no content
if _checkConditionalGET(view, extra_context={}):
return ''
# call caching policy manager.
_setCacheHeaders(view, {})
return result
elif document is not None:
return document()
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, '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()
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
if result is not None:
result = result.__of__(self)
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()
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
if result is not None and not kw.get('src__', 0):
result = [doc.__of__(self) for doc in result]
return result
security.declareProtected(Permissions.View, 'getPermanentURL')
def getPermanentURL(self, document, view=True):
"""
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
XXX The following argument is obsoleted because we no longer need /view.
If view is True, the url returned point to html content and can be
opened in a browser (ie. + '/view' for ooo documents)
"""
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getPermanentURL', self, document.getPath())
try:
return cache[key]
except KeyError:
pass
document = document.getObject().__of__(self)
result = document._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=None):
"""
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
"""
if document is None:
document = self
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getBreadcrumbItemList', 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
security.declareProtected(Permissions.View, 'getSiteMapTree')
def getSiteMapTree(self, **kw):
"""
Return a site map tree section dependent breadcrumb in the
form of a list of dicts whose structure is provided as a tree
so that it is easy to implement recursive call with TAL/METAL:
[
{
'url' : '/erp5/web_site_module/site/section',
'level' : 1,
'translated_title' : 'Section Title',
'subsection' : [
{
'url' : '/erp5/web_site_module/site/section/reference',
'level' : 2,
'translated_title' : 'Sub Section Title',
'subsection' : None,
},
...
],
}
...
]
This method must be implemented through a
portal type dependent script:
WebSection_getSiteMapTree
"""
cache = getReadOnlyTransactionCache()
if cache is not None:
key = ('getSiteMapTree', self) + tuple(kw.items())
try:
return cache[key]
except KeyError:
pass
result = self._getTypeBasedMethod('getSiteMapTree',
fallback_script_id='WebSection_getSiteMapTree')(**kw)
if cache is not None:
cache[key] = result
return result
def _edit(self, **kw):
# XXX it is unclear if we should keep this behavior in other potential subclasses.
# Probably yes.
if self.getPortalType() in WEB_SECTION_PORTAL_TYPE_TUPLE:
if getattr(self, '__before_traverse__', None) is None:
# migrate beforeTraverse hook if missing
handle = self.meta_type + '/' + self.getId()
BeforeTraverse.registerBeforeTraverse(self, self._getTraversalHookClass()(), handle)
else:
# cleanup beforeTraverse hooks that may exist after this document was cloned.
self._cleanupBeforeTraverseHooks()
super(WebSection, self)._edit(**kw)
......@@ -25,23 +25,17 @@
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.ERP5.Document.WebSection import WebSection
from Products.ERP5.Document.WebSection import WebSectionTraversalHook
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.Globals import get_request
from Persistence import Persistent
from ZPublisher import BeforeTraverse
from ZPublisher.HTTPRequest import HTTPRequest
from warnings import warn
from zExceptions import Redirect
WEBSITE_KEY = 'web_site_value'
WEBSITE_LANGUAGE_KEY = 'web_site_language'
# WebSite Document class has been migrated to ZODB Components and this module
# has been kept here because of WebSiteTraversalHook which is not an ERP5
# object
kept_for_backward_compatibility_only = True
class WebSiteTraversalHook(WebSectionTraversalHook):
"""Traversal Hook for websites
......@@ -130,150 +124,3 @@ class WebSiteTraversalHook(WebSectionTraversalHook):
request['AcceptLanguage'].set(selected_language, 80)
elif default_language:
request['AcceptLanguage'].set(default_language, 80)
class WebSite(WebSection):
"""
The Web Site root class is specialises WebSection
by defining a global webmaster user.
"""
# CMF Type Definition
meta_type = 'ERP5 Web Site'
portal_type = 'Web Site'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.WebSection
, PropertySheet.WebSite
, PropertySheet.Predicate
)
web_section_key = WEBSITE_KEY
security.declareProtected(Permissions.AccessContentsInformation, 'getWebSiteValue')
def getWebSiteValue(self):
"""
Returns the current web site (ie. self) though containment acquisition
"""
return self
# Static Language Selection support
def getExtensibleContent(self, request, name):
language_list = self.getAvailableLanguageList()
if language_list and self.isStaticLanguageSelection():
# Interprete names which could be a language
# as a language selection only if language_list
# was defined or set default language
if name in language_list:
default_language = self.getDefaultAvailableLanguage()
if request.get('AcceptLanguage') is not None:
request['AcceptLanguage'].set(name, 100)
request.set(WEBSITE_LANGUAGE_KEY, name)
if self.isTempObject() or name == default_language:
redirect_path_list = [self.getOriginalDocument().absolute_url()]
if name != default_language:
redirect_path_list.append(name)
redirect_path_list.extend(reversed(request['TraversalRequestNameStack']))
request['minimum_language_redirect_url'] = '/'.join(redirect_path_list)
query_string = request.get('QUERY_STRING')
if query_string:
request['minimum_language_redirect_url'] += '?' + query_string
return self.getOriginalDocument().asContext(id=name, __language_web_site=True)
return WebSection.getExtensibleContent(self, request, name)
security.declarePublic('isSubtreeIndexable')
def isSubtreeIndexable(self):
if self.isTempObject() and getattr(self, '__language_web_site', False):
# temp Web Site used to select a language must not prevent
# document indexation
return self.aq_inner.aq_parent.isSubtreeIndexable()
return super(WebSite, self).isSubtreeIndexable()
def _getExtensibleContent(self, request, name):
"""
Legacy API
"""
warn("_getExtensibleContent() function is deprecated. Use getExtensibleContent() instead.", \
DeprecationWarning, stacklevel=2)
return self.getExtensibleContent(request, name)
def _getTraversalHookClass(self):
return WebSiteTraversalHook
def __before_publishing_traverse__(self, self2, request):
redirect_url = request.get('minimum_language_redirect_url')
if redirect_url:
raise Redirect(redirect_url)
return super(WebSite, self).__before_publishing_traverse__(self2, request)
security.declareProtected(Permissions.AccessContentsInformation, 'getPermanentURLList')
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
"""
return map(lambda x:x.getPermanentURL(document), self.getWebSectionValueList(document))
security.declareProtected(Permissions.AccessContentsInformation, 'getWebSectionValueList')
def getWebSectionValueList(self, document):
"""
Returns a list of sections which a given document is
part of.
This could be implemented either by testing all sections
and building a cache or by using the predicate API
to find which sections apply.
"""
def getWebSectionUidList(section):
# Only return visible web section
if section.isVisible():
result = [section.getUid()]
else:
result = []
for o in section.contentValues(portal_type='Web Section'):
result.extend(getWebSectionUidList(o))
return result
_getWebSectionUidList = CachingMethod(getWebSectionUidList,
id='WebSite._getWebSectionUidList',
cache_factory='erp5_content_medium')
web_section_uid_list = _getWebSectionUidList(self)
if web_section_uid_list:
section_list = self.portal_domains.searchPredicateList(document,
portal_type='Web Section',
uid=web_section_uid_list)
section_dict = {}
for section in section_list:
section_dict[section.getPhysicalPath()] = section
# Eliminate path
for section in section_list:
path = section.getPhysicalPath()
for i in range(0, len(path)):
sub_path = tuple(path[0:i])
if section_dict.has_key(sub_path):
del section_dict[sub_path]
section_list = section_dict.values()
# Sort by Index
section_list.sort(key=lambda x: x.getIntIndex())
return section_list
else:
return []
......@@ -992,6 +992,11 @@ def importLocalDocument(class_id, path=None, class_path=None):
assert path is None
module_path = class_path.rsplit('.', 1)[0]
module = __import__(module_path, {}, {}, (module_path,))
try:
klass = getattr(module, class_id)
except AttributeError:
assert hasattr(module, 'kept_for_backward_compatibility_only')
return
else:
# local document in INSTANCE_HOME/Document/
# (created by ClassTool?)
......@@ -1022,7 +1027,6 @@ def importLocalDocument(class_id, path=None, class_path=None):
### newTempFoo
temp_document_constructor_name = "newTemp%s" % class_id
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
klass = getattr(module, class_id)
temp_document_constructor = deprecated(
('newTemp*(self, ID) will be removed, use self.newContent('
'temp_object=True, id=ID, portal_type=...)'))(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment