Commit dfb15874 authored by Julien Muchembled's avatar Julien Muchembled

Add support for CMF 2.3

See merge request !957
parents 989ad1fd 9f248dfc
Pipeline #13519 failed with stage
in 0 seconds
...@@ -6899,17 +6899,17 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6899,17 +6899,17 @@ class TestBusinessTemplate(BusinessTemplateMixin):
Updating from: Updating from:
portal_categories/test_category/modified portal_categories/test_category/modified_category
portal_categories/test_category/modified/container_in_which_child_is_added portal_categories/test_category/modified_category/container_in_which_child_is_added
portal_categories/test_category/modified/container_in_which_child_is_added/child_kept portal_categories/test_category/modified_category/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified/removed portal_categories/test_category/modified_category/removed
to: to:
portal_categories/test_category/modified <-- this will be modified portal_categories/test_category/modified_category <-- this will be modified
portal_categories/test_category/modified/container_in_which_child_is_added portal_categories/test_category/modified_category/container_in_which_child_is_added
portal_categories/test_category/modified/container_in_which_child_is_added/child_kept portal_categories/test_category/modified_category/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified/container_in_which_child_is_added/added portal_categories/test_category/modified_category/container_in_which_child_is_added/added
was causing when test_category was added both as a base category and as paths. was causing when test_category was added both as a base category and as paths.
...@@ -6920,7 +6920,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6920,7 +6920,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
portal_categories.manage_delObjects(['test_category']) portal_categories.manage_delObjects(['test_category'])
base_category = portal_categories.newContent(portal_type='Base Category', id='test_category') base_category = portal_categories.newContent(portal_type='Base Category', id='test_category')
parent_category = base_category.newContent(portal_type='Category', id='modified') parent_category = base_category.newContent(portal_type='Category', id='modified_category')
parent_category.newContent(portal_type='Category', id='container_in_which_child_is_added') parent_category.newContent(portal_type='Category', id='container_in_which_child_is_added')
parent_category.newContent(portal_type='Category', id='removed') parent_category.newContent(portal_type='Category', id='removed')
parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='child_kept') parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='child_kept')
...@@ -6945,7 +6945,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6945,7 +6945,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
shutil.rmtree(export_dir) shutil.rmtree(export_dir)
# Apply the changes and build a second version of business template # Apply the changes and build a second version of business template
parent_category.setTitle('modified') parent_category.setTitle('modified_category')
parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='added') parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='added')
parent_category.manage_delObjects(['removed']) parent_category.manage_delObjects(['removed'])
...@@ -6959,7 +6959,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6959,7 +6959,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
self.tic() self.tic()
new_business_template_version_1.install() new_business_template_version_1.install()
self.tic() self.tic()
portal_categories.test_category.modified.container_in_which_child_is_added.setTitle( portal_categories.test_category.modified_category.container_in_which_child_is_added.setTitle(
'This path should not be reinstalled during update' 'This path should not be reinstalled during update'
) )
self.tic() self.tic()
...@@ -6971,7 +6971,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6971,7 +6971,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
self.tic() self.tic()
self.assertEqual( self.assertEqual(
'This path should not be reinstalled during update', 'This path should not be reinstalled during update',
portal_categories.test_category.modified.container_in_which_child_is_added.getTitle()) portal_categories.test_category.modified_category.container_in_which_child_is_added.getTitle())
def test_update_business_template_with_template_keep_path_list_catalog_method(self): def test_update_business_template_with_template_keep_path_list_catalog_method(self):
"""Tests for `template_keep_path_list` feature for the special case of catalog methods """Tests for `template_keep_path_list` feature for the special case of catalog methods
......
...@@ -576,7 +576,7 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -576,7 +576,7 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
if i.getId() not in ('portal_uidhandler',) and if i.getId() not in ('portal_uidhandler',) and
0 != i.getUid() != i.getProperty('uid')]) 0 != i.getUid() != i.getProperty('uid')])
def test_site_manager_and_translation_migration(self): def test_04_site_manager_and_translation_migration(self):
from zope.site.hooks import getSite, setSite from zope.site.hooks import getSite, setSite
from zope.component import queryUtility from zope.component import queryUtility
from zope.i18n.interfaces import ITranslationDomain from zope.i18n.interfaces import ITranslationDomain
...@@ -594,25 +594,27 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -594,25 +594,27 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
self.assertEqual(queryUtility(ITranslationDomain, 'ui'), None) self.assertEqual(queryUtility(ITranslationDomain, 'ui'), None)
# now let's simulate a site just migrated from Zope 2.8 that's being # now let's simulate a site just migrated from Zope 2.8 that's being
# accessed for the first time: # accessed for the first time:
old_site = getSite() from Products.ERP5 import ERP5Site
try: if 1: # BBB
setSite() setSite()
# Sites from Zope2.8 don't have a site_manager yet. # Sites from Zope2.8 don't have a site_manager yet.
del self.portal._components del self.portal._components
self.assertIsNotNone(ERP5Site._missing_tools_registered)
ERP5Site._missing_tools_registered = None
self.commit()
# check that we can't get any translation utility # check that we can't get any translation utility
self.assertEqual(queryUtility(ITranslationDomain, 'erp5_ui'), None) self.assertEqual(queryUtility(ITranslationDomain, 'erp5_ui'), None)
# Now simulate first access. Default behaviour from # Now simulate first access. Default behaviour from
# ObjectManager is to raise a ComponentLookupError here: # ObjectManager is to raise a ComponentLookupError here:
setSite(self.portal) setSite(self.portal)
self.commit()
self.assertIsNotNone(ERP5Site._missing_tools_registered)
# This should have automatically reconstructed the i18n utility # This should have automatically reconstructed the i18n utility
# registrations: # registrations:
self.assertEqual(queryUtility(ITranslationDomain, 'erp5_ui'), self.assertEqual(queryUtility(ITranslationDomain, 'erp5_ui'),
erp5_ui_catalog) erp5_ui_catalog)
self.assertEqual(queryUtility(ITranslationDomain, 'ui'), erp5_ui_catalog) self.assertEqual(queryUtility(ITranslationDomain, 'ui'), erp5_ui_catalog)
finally:
# clean everything up, we don't want to mess the test environment
self.abort()
setSite(old_site)
def test_BasicAuthenticateDesactivated(self): def test_BasicAuthenticateDesactivated(self):
"""Make sure Unauthorized error does not lead to Basic auth popup in browser""" """Make sure Unauthorized error does not lead to Basic auth popup in browser"""
......
...@@ -52,6 +52,7 @@ from AccessControl.ZopeGuards import guarded_getattr, guarded_hasattr ...@@ -52,6 +52,7 @@ from AccessControl.ZopeGuards import guarded_getattr, guarded_hasattr
from Products.ERP5Type.tests.utils import createZODBPythonScript from Products.ERP5Type.tests.utils import createZODBPythonScript
from Products.ERP5Type.tests.utils import removeZODBPythonScript from Products.ERP5Type.tests.utils import removeZODBPythonScript
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from DateTime import DateTime
class PropertySheetTestCase(ERP5TypeTestCase): class PropertySheetTestCase(ERP5TypeTestCase):
"""Base test case class for property sheets tests. """Base test case class for property sheets tests.
...@@ -2478,8 +2479,9 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor): ...@@ -2478,8 +2479,9 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor):
portal = self.getPortalObject() portal = self.getPortalObject()
folder = self.getOrganisationModule() folder = self.getOrganisationModule()
obj = folder.newContent(portal_type='Organisation') obj = folder.newContent(portal_type='Organisation')
self.assertNotEquals(obj.getCreationDate(), portal.CreationDate()) self.assertIsInstance(portal.creation_date, DateTime)
self.assertNotEquals(obj.getCreationDate(), folder.getCreationDate()) self.assertLess(portal.creation_date, obj.getCreationDate())
self.assertIsNone(folder.getCreationDate())
def test_copyWithoutModificationRight(self): def test_copyWithoutModificationRight(self):
""" """
......
...@@ -638,9 +638,6 @@ class TestProxyField(ERP5TypeTestCase): ...@@ -638,9 +638,6 @@ class TestProxyField(ERP5TypeTestCase):
self.container = Folder('container').__of__(self.portal) self.container = Folder('container').__of__(self.portal)
self.container.manage_addProduct['ERP5Form'].addERP5Form('Base_viewProxyFieldLibrary', 'Proxys') self.container.manage_addProduct['ERP5Form'].addERP5Form('Base_viewProxyFieldLibrary', 'Proxys')
self.container.manage_addProduct['ERP5Form'].addERP5Form('Base_view', 'View') self.container.manage_addProduct['ERP5Form'].addERP5Form('Base_view', 'View')
from Products.CMFCore.tests.base.utils import _setUpDefaultTraversable
_setUpDefaultTraversable()
def addField(self, form, id_, title, field_type): def addField(self, form, id_, title, field_type):
form.manage_addField(id_, title, field_type) form.manage_addField(id_, title, field_type)
......
...@@ -17,6 +17,8 @@ Save, download or return generated PDF Document ...@@ -17,6 +17,8 @@ Save, download or return generated PDF Document
# doc_pdf_file pdf content to store # doc_pdf_file pdf content to store
# doc_aggregate_list not applicable (only used for events) # doc_aggregate_list not applicable (only used for events)
from io import BytesIO
if doc_save: if doc_save:
dms_module = getattr(context, 'document_module', None) dms_module = getattr(context, 'document_module', None)
if dms_module is not None: if dms_module is not None:
...@@ -31,7 +33,7 @@ if doc_save: ...@@ -31,7 +33,7 @@ if doc_save:
) )
document.edit( document.edit(
source_reference=''.join([doc_reference, '.pdf']), source_reference=''.join([doc_reference, '.pdf']),
file=doc_pdf_file file=BytesIO(doc_pdf_file)
) )
document.setContentType("application/pdf") document.setContentType("application/pdf")
......
portal_properties | csv_export portal_actions | csv_export
portal_properties | csv_import portal_actions | csv_import
\ No newline at end of file \ No newline at end of file
...@@ -672,10 +672,9 @@ class TestDocument(TestDocumentMixin): ...@@ -672,10 +672,9 @@ class TestDocument(TestDocumentMixin):
for document_type in portal.getPortalDocumentTypeList(): for document_type in portal.getPortalDocumentTypeList():
module = portal.getDefaultModule(document_type) module = portal.getDefaultModule(document_type)
obj = module.newContent(portal_type=document_type) obj = module.newContent(portal_type=document_type)
self.assertNotEquals(obj.getCreationDate(), self.assertIsInstance(portal.creation_date, DateTime)
module.getCreationDate()) self.assertLess(portal.creation_date, obj.getCreationDate())
self.assertNotEquals(obj.getCreationDate(), self.assertIsNone(module.getCreationDate())
portal.CreationDate())
def test_06_ProcessingStateOfAClonedDocument(self): def test_06_ProcessingStateOfAClonedDocument(self):
""" """
...@@ -2865,7 +2864,7 @@ class TestDocumentWithSecurity(TestDocumentMixin): ...@@ -2865,7 +2864,7 @@ class TestDocumentWithSecurity(TestDocumentMixin):
reference='Foo_001', reference='Foo_001',
title='Foo_OO1') title='Foo_OO1')
f = makeFileUpload('Foo_001.odt') f = makeFileUpload('Foo_001.odt')
text_document.edit(file=f.read()) text_document.edit(file=f)
f.close() f.close()
self.tic() self.tic()
......
...@@ -186,6 +186,8 @@ class TestWebDavSupport(ERP5TypeTestCase): ...@@ -186,6 +186,8 @@ class TestWebDavSupport(ERP5TypeTestCase):
""" """
iso_text_content = text_content.decode('utf-8').encode('iso-8859-1') iso_text_content = text_content.decode('utf-8').encode('iso-8859-1')
path = web_page_module.getPath() path = web_page_module.getPath()
for _ in xrange(2): # Run twice to check the code that compares
# old & new data when setting file attribute.
response = self.publish('%s/%s' % (path, filename), response = self.publish('%s/%s' % (path, filename),
request_method='PUT', request_method='PUT',
stdin=StringIO(iso_text_content), stdin=StringIO(iso_text_content),
......
...@@ -34,6 +34,7 @@ import random ...@@ -34,6 +34,7 @@ import random
import tempfile import tempfile
from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import getDOMImplementation
from App.config import getConfiguration from App.config import getConfiguration
from Products.CMFCore.ActionsTool import ActionsTool
from Products.ERP5.Document.BusinessTemplate import \ from Products.ERP5.Document.BusinessTemplate import \
BusinessTemplateMissingDependency BusinessTemplateMissingDependency
...@@ -51,7 +52,6 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -51,7 +52,6 @@ class TestTemplateTool(ERP5TypeTestCase):
return ('erp5_core_proxy_field_legacy', return ('erp5_core_proxy_field_legacy',
'erp5_full_text_mroonga_catalog', 'erp5_full_text_mroonga_catalog',
'erp5_base', 'erp5_base',
'erp5_csv_style',
'erp5_crm', 'erp5_crm',
'erp5_forge') 'erp5_forge')
...@@ -140,7 +140,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -140,7 +140,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(test_web.getTitle(), 'test_web') self.assertEqual(test_web.getTitle(), 'test_web')
self.assertEqual(len(test_web.getRevision()), 28) self.assertEqual(len(test_web.getRevision()), 28)
def test_updateBusinessTemplateFromUrl_simple(self): def test_00_updateBusinessTemplateFromUrl_simple(self):
""" """
Test updateBusinessTemplateFromUrl method Test updateBusinessTemplateFromUrl method
...@@ -148,13 +148,42 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -148,13 +148,42 @@ class TestTemplateTool(ERP5TypeTestCase):
the new bt5 is not installed, only imported. the new bt5 is not installed, only imported.
""" """
self._svn_setup_ssl() self._svn_setup_ssl()
global PropertiesTool
class PropertiesTool(ActionsTool):
id = 'portal_properties'
cls = PropertiesTool
# Assign a fake properties tool to the portal
tool = PropertiesTool()
self.portal._setObject(tool.id, tool, set_owner=False, suppress_events=True)
del tool
self.commit()
template_tool = self.portal.portal_templates template_tool = self.portal.portal_templates
url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style'
template_tool.updateBusinessTemplateFromUrl(url)
old_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style') old_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
# fake different revision # fake different revision
old_bt.setRevision('') old_bt.setRevision('')
url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style'
template_tool.updateBusinessTemplateFromUrl(url) # Break the properties tool
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style') self.assertIs(self.portal.portal_properties.__class__, cls)
self.commit()
self.portal._p_jar.cacheMinimize()
del PropertiesTool
self.assertIsNot(self.portal.portal_properties.__class__, cls)
# Remove portal.portal_properties
from Products.ERP5Type.dynamic.portal_type_class import \
_bootstrapped, synchronizeDynamicModules
_bootstrapped.remove(self.portal.id)
synchronizeDynamicModules(self.portal, force=True)
# The bt from this repo
url = self._getBTPathAndIdList(('erp5_csv_style',))[0][0]
new_bt = template_tool.updateBusinessTemplateFromUrl(url)
self.assertNotEquals(old_bt, new_bt) self.assertNotEquals(old_bt, new_bt)
self.assertEqual('erp5_csv_style', new_bt.getTitle()) self.assertEqual('erp5_csv_style', new_bt.getTitle())
......
...@@ -1869,6 +1869,7 @@ class ToolTemplateItem(PathTemplateItem): ...@@ -1869,6 +1869,7 @@ class ToolTemplateItem(PathTemplateItem):
def install(self, context, trashbin, **kw): def install(self, context, trashbin, **kw):
""" When we install a tool that is a type provider not """ When we install a tool that is a type provider not
registered on types tool, register it into the type provider. registered on types tool, register it into the type provider.
We also need to register the tool on the site manager
""" """
PathTemplateItem.install(self, context, trashbin, **kw) PathTemplateItem.install(self, context, trashbin, **kw)
portal = context.getPortalObject() portal = context.getPortalObject()
...@@ -1879,6 +1880,8 @@ class ToolTemplateItem(PathTemplateItem): ...@@ -1879,6 +1880,8 @@ class ToolTemplateItem(PathTemplateItem):
type_container_id not in types_tool.type_provider_list): type_container_id not in types_tool.type_provider_list):
types_tool.type_provider_list = tuple(types_tool.type_provider_list) + \ types_tool.type_provider_list = tuple(types_tool.type_provider_list) + \
(type_container_id,) (type_container_id,)
tool_id_list = list(set(self._objects.keys()) & set(portal._registry_tool_id_list))
portal._registerTools(tool_id_list)
def uninstall(self, context, **kw): def uninstall(self, context, **kw):
""" When we uninstall a tool, unregister it from the type provider. """ """ When we uninstall a tool, unregister it from the type provider. """
...@@ -2121,7 +2124,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem): ...@@ -2121,7 +2124,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
update_dict = kw.get('object_to_update') update_dict = kw.get('object_to_update')
force = kw.get('force') force = kw.get('force')
portal = context.getPortalObject() portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins') skin_tool = portal.portal_skins
for skin_folder_id in self._objects.keys(): for skin_folder_id in self._objects.keys():
...@@ -2163,7 +2166,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem): ...@@ -2163,7 +2166,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
def uninstall(self, context, **kw): def uninstall(self, context, **kw):
portal = context.getPortalObject() portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins') skin_tool = portal.portal_skins
object_path = kw.get('object_path') object_path = kw.get('object_path')
for skin_folder_id in (object_path,) if object_path else self._objects: for skin_folder_id in (object_path,) if object_path else self._objects:
skin_selection_list = self._objects[skin_folder_id] skin_selection_list = self._objects[skin_folder_id]
......
This diff is collapsed.
...@@ -34,8 +34,9 @@ from erp5.component.document.Document import Document, VALID_TEXT_FORMAT_LIST ...@@ -34,8 +34,9 @@ from erp5.component.document.Document import Document, VALID_TEXT_FORMAT_LIST
from erp5.component.document.Document import VALID_IMAGE_FORMAT_LIST from erp5.component.document.Document import VALID_IMAGE_FORMAT_LIST
from erp5.component.document.Document import ConversionError from erp5.component.document.Document import ConversionError
from Products.ERP5Type.Base import Base, removeIContentishInterface from Products.ERP5Type.Base import Base, removeIContentishInterface
from Products.CMFDefault.File import File as CMFFile from OFS.Image import File as OFS_File
from Products.ERP5Type.Utils import deprecated from Products.ERP5Type.Utils import deprecated
from cStringIO import StringIO
def _unpackData(data): def _unpackData(data):
""" """
...@@ -46,7 +47,7 @@ def _unpackData(data): ...@@ -46,7 +47,7 @@ def _unpackData(data):
_MARKER = object() _MARKER = object()
class File(Document, CMFFile): class File(Document, OFS_File):
""" """
A File can contain raw data which can be uploaded and downloaded. A File can contain raw data which can be uploaded and downloaded.
It is the root class of Image, OOoDocument (ERP5OOo product), It is the root class of Image, OOoDocument (ERP5OOo product),
...@@ -110,8 +111,14 @@ class File(Document, CMFFile): ...@@ -110,8 +111,14 @@ class File(Document, CMFFile):
filename = kw.get('filename') filename = kw.get('filename')
if filename: if filename:
self._setFilename(filename) self._setFilename(filename)
if self._isNotEmpty(file_object): if file_object is not None:
self._setFile(file_object, precondition=precondition) # XXX: Rather than doing nothing if empty, consider changing:
# - _update_image_info to clear metadata
# - interactions to do nothing (or else?)
file_object.seek(0, 2)
if file_object.tell():
file_object.seek(0)
self._setFile(file_object)
Base._edit(self, **kw) Base._edit(self, **kw)
security.declareProtected( Permissions.ModifyPortalContent, 'edit' ) security.declareProtected( Permissions.ModifyPortalContent, 'edit' )
...@@ -138,11 +145,18 @@ class File(Document, CMFFile): ...@@ -138,11 +145,18 @@ class File(Document, CMFFile):
return None return None
def _setFile(self, data, precondition=None): def _setFile(self, data, precondition=None):
if data is not None and self.hasData() and \ if data is None:
str(data.read()) == str(self.getData()): return
# Same data as previous, no need to change it's content if self.hasData():
if str(data.read()) == str(self.getData()):
# Same data as previous, no need to change its content
return return
CMFFile._edit(self, precondition=precondition, file=data) else:
data.seek(0, 2)
if data.tell():
data.seek(0)
self.manage_upload(data)
security.declareProtected(Permissions.ModifyPortalContent,'setFile') security.declareProtected(Permissions.ModifyPortalContent,'setFile')
def setFile(self, data, precondition=None): def setFile(self, data, precondition=None):
...@@ -176,11 +190,16 @@ class File(Document, CMFFile): ...@@ -176,11 +190,16 @@ class File(Document, CMFFile):
return str(data) return str(data)
# DAV Support # DAV Support
PUT = CMFFile.PUT security.declareProtected(Permissions.ModifyPortalContent, 'PUT')
def PUT(self, REQUEST, RESPONSE):
"""from Products.CMFDefault.File"""
OFS_File.PUT(self, REQUEST, RESPONSE)
self.reindexObject()
security.declareProtected(Permissions.FTPAccess, 'manage_FTPstat', security.declareProtected(Permissions.FTPAccess, 'manage_FTPstat',
'manage_FTPlist') 'manage_FTPlist')
manage_FTPlist = CMFFile.manage_FTPlist manage_FTPlist = OFS_File.manage_FTPlist
manage_FTPstat = CMFFile.manage_FTPstat manage_FTPstat = OFS_File.manage_FTPstat
security.declareProtected(Permissions.AccessContentsInformation, 'getMimeTypeAndContent') security.declareProtected(Permissions.AccessContentsInformation, 'getMimeTypeAndContent')
def getMimeTypeAndContent(self): def getMimeTypeAndContent(self):
......
...@@ -23,6 +23,7 @@ from Products.ERP5Type.Globals import InitializeClass ...@@ -23,6 +23,7 @@ from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.CMFCore.PortalContent import ResourceLockedError from Products.CMFCore.PortalContent import ResourceLockedError
from zExceptions import Forbidden from zExceptions import Forbidden
from cStringIO import StringIO
security = ModuleSecurityInfo(__name__) security = ModuleSecurityInfo(__name__)
...@@ -76,7 +77,7 @@ class TextContent: ...@@ -76,7 +77,7 @@ class TextContent:
headers = self.parseHeadersFromText(body) headers = self.parseHeadersFromText(body)
content_type = REQUEST.get_header('Content-Type', '') content_type = REQUEST.get_header('Content-Type', '')
headers.setdefault('content_type', content_type) headers.setdefault('content_type', content_type)
headers['file'] = body headers['file'] = StringIO(body)
self._edit(**headers) self._edit(**headers)
except ResourceLockedError: except ResourceLockedError:
transaction.abort() transaction.abort()
......
...@@ -85,7 +85,6 @@ print reindex( ...@@ -85,7 +85,6 @@ print reindex(
'portal_classes', 'portal_classes',
'portal_preferences', 'portal_preferences',
'portal_simulation', 'portal_simulation',
'portal_uidhandler',
) and ) and
'inventory' not in x.id 'inventory' not in x.id
], ],
......
"Modified version for ERP5 to append the default action (/view) in the URL." "Modified version for ERP5 to append the default action (/view) in the URL."
from Products.CMFCore.utils import getToolByName portal = context.getPortalObject()
ptool = getToolByName(script, 'portal_properties')
utool = getToolByName(script, 'portal_url') utool = portal.portal_url
portal_url = utool() portal_url = utool()
result = [] param = '?ignore_layout:int=1' if int(portal.REQUEST.get('ignore_layout', 0)) else ''
param = int(context.REQUEST.get('ignore_layout', 0)) and '?ignore_layout:int=1' or ''
if include_root: if include_root:
result.append( { 'id' : 'root' result = [{
, 'title' : ptool.title() 'id' : 'root',
, 'url' : '%s/view%s' % (portal_url, param) 'title' : portal.title,
} 'url' : '%s/view%s' % (portal_url, param),
) }]
else:
relative = utool.getRelativeContentPath(context) result = []
portal = utool.getPortalObject()
obj = portal obj = portal
now = [] now = []
for name in relative: for name in utool.getRelativeContentPath(context):
obj = obj.restrictedTraverse(name) obj = obj.restrictedTraverse(name)
now.append(name) now.append(name)
title = ( title = (
getattr(obj, "getCompactTranslatedTitle", lambda: None)() or getattr(obj, "getCompactTranslatedTitle", lambda: None)() or
obj.getTitle() or obj.getId() obj.getTitle() or obj.getId()
) )
if not name == 'talkback': if name != 'talkback':
result.append( { 'id' : name result.append( { 'id' : name
, 'title' : title , 'title' : title
, 'url' : '%s/%s/view%s' % (portal_url, '/'.join(now), param) , 'url' : '%s/%s/view%s' % (portal_url, '/'.join(now), param)
......
...@@ -69,7 +69,6 @@ class TestFormPrintoutAsODG(TestFormPrintoutMixin): ...@@ -69,7 +69,6 @@ class TestFormPrintoutAsODG(TestFormPrintoutMixin):
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
if custom._getOb('Foo_getODGStyleSheet', None) is None: if custom._getOb('Foo_getODGStyleSheet', None) is None:
addStyleSheet(id='Foo_getODGStyleSheet', file=foo_file, title='', addStyleSheet(id='Foo_getODGStyleSheet', file=foo_file, title='',
precondition='',
content_type='application/vnd.oasis.opendocument.graphics') content_type='application/vnd.oasis.opendocument.graphics')
erp5OOo = custom.manage_addProduct['ERP5OOo'] erp5OOo = custom.manage_addProduct['ERP5OOo']
......
...@@ -84,23 +84,22 @@ class TestFormPrintoutAsODT(TestFormPrintoutMixin): ...@@ -84,23 +84,22 @@ class TestFormPrintoutAsODT(TestFormPrintoutMixin):
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
if custom._getOb('Foo_getODTStyleSheet', None) is None: if custom._getOb('Foo_getODTStyleSheet', None) is None:
addStyleSheet(id='Foo_getODTStyleSheet', file=foo_file, title='', addStyleSheet(id='Foo_getODTStyleSheet', file=foo_file, title='',
precondition='', content_type = 'application/vnd.oasis.opendocument.text') content_type = 'application/vnd.oasis.opendocument.text')
if custom._getOb('Foo2_getODTStyleSheet', None) is None: if custom._getOb('Foo2_getODTStyleSheet', None) is None:
addStyleSheet(id='Foo2_getODTStyleSheet', file=foo2_file, title='', addStyleSheet(id='Foo2_getODTStyleSheet', file=foo2_file, title='',
precondition='', content_type = 'application/vnd.oasis.opendocument.text') content_type = 'application/vnd.oasis.opendocument.text')
if custom._getOb('Foo3_getODTStyleSheet', None) is None: if custom._getOb('Foo3_getODTStyleSheet', None) is None:
addStyleSheet(id='Foo3_getODTStyleSheet', file=foo3_file, title='', addStyleSheet(id='Foo3_getODTStyleSheet', file=foo3_file, title='',
precondition='', content_type = 'application/vnd.oasis.opendocument.text') content_type = 'application/vnd.oasis.opendocument.text')
if custom._getOb('Foo4_getODTStyleSheet', None) is None: if custom._getOb('Foo4_getODTStyleSheet', None) is None:
addStyleSheet(id='Foo4_getODTStyleSheet', file=foo4_file, title='', addStyleSheet(id='Foo4_getODTStyleSheet', file=foo4_file, title='',
precondition='', content_type = 'application/vnd.oasis.opendocument.text') content_type = 'application/vnd.oasis.opendocument.text')
if custom._getOb('Foo5_getODTStyleSheet', None) is None: if custom._getOb('Foo5_getODTStyleSheet', None) is None:
addStyleSheet(id='Foo5_getODTStyleSheet', file=foo5_file, title='', addStyleSheet(id='Foo5_getODTStyleSheet', file=foo5_file, title='',
precondition='', content_type = 'application/vnd.oasis.opendocument.text') content_type = 'application/vnd.oasis.opendocument.text')
if custom._getOb('Foo_getVariableODTStyleSheet', None) is None: if custom._getOb('Foo_getVariableODTStyleSheet', None) is None:
addStyleSheet(id='Foo_getVariableODTStyleSheet', addStyleSheet(id='Foo_getVariableODTStyleSheet',
file=variable_file_object, title='', file=variable_file_object, title='',
precondition='',
content_type='application/vnd.oasis.opendocument.text') content_type='application/vnd.oasis.opendocument.text')
erp5OOo = custom.manage_addProduct['ERP5OOo'] erp5OOo = custom.manage_addProduct['ERP5OOo']
......
...@@ -66,7 +66,7 @@ class TestOoodResponse(ERP5TypeTestCase): ...@@ -66,7 +66,7 @@ class TestOoodResponse(ERP5TypeTestCase):
custom = portal_skins.custom custom = portal_skins.custom
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
addStyleSheet(id='Base_getODTStyleSheet', file=import_file, title='', addStyleSheet(id='Base_getODTStyleSheet', file=import_file, title='',
precondition='', content_type='application/vnd.oasis.opendocument.text') content_type='application/vnd.oasis.opendocument.text')
addOOoTemplate = custom.manage_addProduct['ERP5OOo'].addOOoTemplate addOOoTemplate = custom.manage_addProduct['ERP5OOo'].addOOoTemplate
addOOoTemplate(id='ERP5Site_viewNothingAsOdt', title='') addOOoTemplate(id='ERP5Site_viewNothingAsOdt', title='')
portal_skins.changeSkin(skinname=None) portal_skins.changeSkin(skinname=None)
......
...@@ -76,10 +76,10 @@ class TestOooDynamicStyle(ERP5TypeTestCase): ...@@ -76,10 +76,10 @@ class TestOooDynamicStyle(ERP5TypeTestCase):
addStyleSheet = self.getPortal().manage_addProduct['OFSP'].manage_addFile addStyleSheet = self.getPortal().manage_addProduct['OFSP'].manage_addFile
if getattr(self.getPortal(), 'Test_getODTStyleSheet_en', None) is None: if getattr(self.getPortal(), 'Test_getODTStyleSheet_en', None) is None:
addStyleSheet(id='Test_getODTStyleSheet_en', file=en_file, title='', addStyleSheet(id='Test_getODTStyleSheet_en', file=en_file, title='',
precondition='', content_type=self.content_type_writer) content_type=self.content_type_writer)
if getattr(self.getPortal(), 'Test_getODTStyleSheet_ja', None) is None: if getattr(self.getPortal(), 'Test_getODTStyleSheet_ja', None) is None:
addStyleSheet(id='Test_getODTStyleSheet_ja', file=ja_file, title='', addStyleSheet(id='Test_getODTStyleSheet_ja', file=ja_file, title='',
precondition='', content_type=self.content_type_writer) content_type=self.content_type_writer)
if getattr(self.getPortal(), 'Base_getODTStyleSheetByLanguage', None) is None: if getattr(self.getPortal(), 'Base_getODTStyleSheetByLanguage', None) is None:
script_body = """ script_body = """
current_language = context.Localizer.get_selected_language() current_language = context.Localizer.get_selected_language()
......
...@@ -81,7 +81,6 @@ from CopySupport import CopyContainer, CopyError,\ ...@@ -81,7 +81,6 @@ from CopySupport import CopyContainer, CopyError,\
from Errors import DeferredCatalogError, UnsupportedWorkflowMethod from Errors import DeferredCatalogError, UnsupportedWorkflowMethod
from Products.CMFActivity.ActiveObject import ActiveObject from Products.CMFActivity.ActiveObject import ActiveObject
from Products.ERP5Type.Accessor.Accessor import Accessor as Method from Products.ERP5Type.Accessor.Accessor import Accessor as Method
from Products.ERP5Type.Accessor.TypeDefinition import asDate
from Products.ERP5Type.Message import Message from Products.ERP5Type.Message import Message
from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod, super_user from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod, super_user
...@@ -3229,9 +3228,8 @@ class Base( ...@@ -3229,9 +3228,8 @@ class Base(
if history and 'time' in history[0]) if history and 'time' in history[0])
except ValueError: except ValueError:
pass pass
if getattr(aq_base(self), 'CreationDate', None) is not None: if getattr(aq_base(self), 'creation_date', None):
return asDate(self.CreationDate()) return self.creation_date.toZone(DateTime().timezone())
return None # JPS-XXX - try to find a way to return a creation date instead of None
security.declareProtected(Permissions.AccessContentsInformation, 'getModificationDate') security.declareProtected(Permissions.AccessContentsInformation, 'getModificationDate')
def getModificationDate(self): def getModificationDate(self):
...@@ -3260,7 +3258,13 @@ class Base( ...@@ -3260,7 +3258,13 @@ class Base(
# Return a copy of history time, to prevent modification # Return a copy of history time, to prevent modification
return DateTime(max_date) return DateTime(max_date)
if self._p_serial: if self._p_serial:
return DateTime(TimeStamp(self._p_serial).timeTime()) return DateTime(self._p_mtime)
security.declareProtected(Permissions.AccessContentsInformation, 'modified')
def modified(self):
warnings.warn('modified is a deprecated alias to getModificationDate.',
DeprecationWarning)
return self.getModificationDate()
# Layout management # Layout management
security.declareProtected(Permissions.AccessContentsInformation, 'getApplicableLayout') security.declareProtected(Permissions.AccessContentsInformation, 'getApplicableLayout')
......
...@@ -34,6 +34,7 @@ from Products.ERP5Type.patches import DCWorkflow ...@@ -34,6 +34,7 @@ from Products.ERP5Type.patches import DCWorkflow
from Products.ERP5Type.patches import Worklists from Products.ERP5Type.patches import Worklists
from Products.ERP5Type.patches import BTreeFolder2 from Products.ERP5Type.patches import BTreeFolder2
from Products.ERP5Type.patches import WorkflowTool from Products.ERP5Type.patches import WorkflowTool
from Products.ERP5Type.patches import DynamicType
from Products.ERP5Type.patches import XMLExportImport from Products.ERP5Type.patches import XMLExportImport
from Products.ERP5Type.patches import ppml from Products.ERP5Type.patches import ppml
from Products.ERP5Type.patches import Expression from Products.ERP5Type.patches import Expression
......
...@@ -189,3 +189,18 @@ import zExceptions ...@@ -189,3 +189,18 @@ import zExceptions
ModuleSecurityInfo('zExceptions').declarePublic(*filter( ModuleSecurityInfo('zExceptions').declarePublic(*filter(
lambda x: Exception in getattr(getattr(zExceptions, x), '__mro__', ()), lambda x: Exception in getattr(getattr(zExceptions, x), '__mro__', ()),
dir(zExceptions))) dir(zExceptions)))
# BBB : allow load of fomer Products.CMFDefault.MembershipTool
# that has been replaced by Products.CMFCore.MembershipTool
try:
from Products.CMFDefault.MembershipTool import MembershipTool
except ImportError:
import sys, imp
m = 'Products.CMFDefault'
sys.modules[m] = imp.new_module(m)
m += ".MembershipTool"
sys.modules[m] = m = imp.new_module(m)
from Products.CMFCore.MembershipTool import MembershipTool
m.MembershipTool = MembershipTool
del m
...@@ -468,6 +468,15 @@ def synchronizeDynamicModules(context, force=False): ...@@ -468,6 +468,15 @@ def synchronizeDynamicModules(context, force=False):
except AttributeError: except AttributeError:
pass # no Activity Tool yet pass # no Activity Tool yet
for tool_id in ("portal_properties", "portal_uidannotation",
"portal_uidgenerator", "portal_uidhandler"):
if portal.hasObject(tool_id):
portal._delObject(tool_id, suppress_events=True)
migrate = True
if tool_id == 'portal_properties':
portal.portal_skins.erp5_xhtml_style.breadcrumbs.write(
'return []')
if migrate: if migrate:
portal.migrateToPortalTypeClass() portal.migrateToPortalTypeClass()
portal.portal_skins.changeSkin(None) portal.portal_skins.changeSkin(None)
......
...@@ -141,9 +141,7 @@ def CMFCoreSkinnableSkinnableObjectManager_changeSkin(self, skinname, REQUEST=No ...@@ -141,9 +141,7 @@ def CMFCoreSkinnableSkinnableObjectManager_changeSkin(self, skinname, REQUEST=No
Patched not to call getSkin. Patched not to call getSkin.
''' '''
if skinname is None: if skinname is None:
sfn = self.getSkinsFolderName() sf = getattr(self, "portal_skins", None)
if sfn is not None:
sf = getattr(self, sfn, None)
if sf is not None: if sf is not None:
skinname = sf.getDefaultSkin() skinname = sf.getDefaultSkin()
tid = get_ident() tid = get_ident()
......
...@@ -13,13 +13,14 @@ ...@@ -13,13 +13,14 @@
############################################################################## ##############################################################################
from Acquisition import aq_parent from Acquisition import aq_parent
from Products.CMFCore.utils import getToolByName, SUBTEMPLATE from Products.CMFCore.utils import SUBTEMPLATE
from zope.component import queryUtility
from Products.CMFCore.interfaces import ICachingPolicyManager
# patch _setCacheHeaders so that existing headers are not overridden # patch _setCacheHeaders so that existing headers are not overridden
def _setCacheHeaders(obj, extra_context): def _setCacheHeaders(obj, extra_context):
"""Set cache headers according to cache policy manager for the obj.""" """Set cache headers according to cache policy manager for the obj."""
REQUEST = getattr(obj, 'REQUEST', None) REQUEST = getattr(obj, 'REQUEST', None)
if REQUEST is not None: if REQUEST is not None:
call_count = getattr(REQUEST, SUBTEMPLATE, 1) - 1 call_count = getattr(REQUEST, SUBTEMPLATE, 1) - 1
setattr(REQUEST, SUBTEMPLATE, call_count) setattr(REQUEST, SUBTEMPLATE, call_count)
...@@ -30,7 +31,7 @@ def _setCacheHeaders(obj, extra_context): ...@@ -30,7 +31,7 @@ def _setCacheHeaders(obj, extra_context):
delattr(REQUEST, SUBTEMPLATE) delattr(REQUEST, SUBTEMPLATE)
content = aq_parent(obj) content = aq_parent(obj)
manager = getToolByName(obj, 'caching_policy_manager', None) manager = queryUtility(ICachingPolicyManager)
if manager is None: if manager is None:
return return
......
...@@ -30,6 +30,7 @@ from App.class_init import InitializeClass ...@@ -30,6 +30,7 @@ from App.class_init import InitializeClass
from Products.CMFCore.CookieCrumbler import CookieCrumbler from Products.CMFCore.CookieCrumbler import CookieCrumbler
from Products.CMFCore.CookieCrumbler import CookieCrumblerDisabled from Products.CMFCore.CookieCrumbler import CookieCrumblerDisabled
from urllib import quote, unquote from urllib import quote, unquote
from zExceptions import Redirect
from ZPublisher.HTTPRequest import HTTPRequest from ZPublisher.HTTPRequest import HTTPRequest
ATTEMPT_NONE = 0 # No attempt at authentication ATTEMPT_NONE = 0 # No attempt at authentication
...@@ -47,6 +48,10 @@ class PatchedCookieCrumbler(CookieCrumbler): ...@@ -47,6 +48,10 @@ class PatchedCookieCrumbler(CookieCrumbler):
security = ClassSecurityInfo() security = ClassSecurityInfo()
CookieCrumbler.auto_login_page = 'login_form'
CookieCrumbler.unauth_page = ''
CookieCrumbler.logout_page = 'logged_out'
def getLoginURL(self): def getLoginURL(self):
''' '''
Redirects to the login page. Redirects to the login page.
...@@ -176,14 +181,175 @@ def modifyRequest(self, req, resp): ...@@ -176,14 +181,175 @@ def modifyRequest(self, req, resp):
CookieCrumbler.modifyRequest = modifyRequest CookieCrumbler.modifyRequest = modifyRequest
def credentialsChanged(self, user, name, pw): def credentialsChanged(self, user, name, pw, request=None):
ac = standard_b64encode('%s:%s' % (name, pw)) """
method = self.getCookieMethod( 'setAuthCookie' Updates cookie credentials if user details are changed.
, self.defaultSetAuthCookie ) """
resp = self.REQUEST['RESPONSE'] if request is None:
method( resp, self.auth_cookie, quote( ac ) ) request = getRequest() # BBB for Membershiptool
reponse = request['RESPONSE']
# <patch>
# We don't want new lines, so use base64.standard_b64encode instead of
# base64.encodestring
ac = standard_b64encode('%s:%s' % (name, pw)).rstrip()
# </patch>
method = self.getCookieMethod('setAuthCookie',
self.defaultSetAuthCookie)
method(reponse, self.auth_cookie, quote(ac))
CookieCrumbler.credentialsChanged = credentialsChanged CookieCrumbler.credentialsChanged = credentialsChanged
# The following patches are to keep the original behaviour of automatic
# redirection to login page. Recent CMF uses a view that is implemented
# in CMFDefault (UnauthorizedView, on zExceptions.Unauthorized).
class ResponseCleanup:
def __init__(self, resp):
self.resp = resp
def __del__(self):
# Free the references.
#
# No errors of any sort may propagate, and we don't care *what*
# they are, even to log them.
try:
del self.resp.unauthorized
except Exception:
pass
try:
del self.resp._unauthorized
except Exception:
pass
try:
del self.resp
except Exception:
pass
if 1:
def __call__(self, container, req):
'''The __before_publishing_traverse__ hook.'''
resp = req['RESPONSE']
try:
attempt = self.modifyRequest(req, resp)
except CookieCrumblerDisabled:
return
# <patch>
if req.get('disable_cookie_login__', 0):
return
if (self.unauth_page or
attempt == ATTEMPT_LOGIN or attempt == ATTEMPT_NONE):
# Modify the "unauthorized" response.
req._hold(ResponseCleanup(resp))
resp.unauthorized = self.unauthorized
resp._unauthorized = self._unauthorized
# </patch>
if attempt != ATTEMPT_NONE:
# Trying to log in or resume a session
if self.cache_header_value:
# we don't want caches to cache the resulting page
resp.setHeader('Cache-Control', self.cache_header_value)
# demystify this in the response.
resp.setHeader('X-Cache-Control-Hdr-Modified-By',
'CookieCrumbler')
phys_path = self.getPhysicalPath()
# <patch>
if self.logout_page:
# Cookies are in use.
page = getattr(container, self.logout_page, None)
if page is not None:
# Provide a logout page.
req._logout_path = phys_path + ('logout',)
req._credentials_changed_path = (
phys_path + ('credentialsChanged',))
# </patch>
def _cleanupResponse(self):
# XXX: this method violates the rules for tools/utilities:
# it depends on self.REQUEST
resp = self.REQUEST['RESPONSE']
# No errors of any sort may propagate, and we don't care *what*
# they are, even to log them.
try: del resp.unauthorized
except Exception: pass
try: del resp._unauthorized
except Exception: pass
return resp
security.declarePrivate('unauthorized')
def unauthorized(self):
resp = self._cleanupResponse()
# If we set the auth cookie before, delete it now.
if resp.cookies.has_key(self.auth_cookie):
del resp.cookies[self.auth_cookie]
# Redirect if desired.
url = self.getUnauthorizedURL()
if url is not None:
raise Redirect, url
# Fall through to the standard unauthorized() call.
resp.unauthorized()
def _unauthorized(self):
resp = self._cleanupResponse()
# If we set the auth cookie before, delete it now.
if resp.cookies.has_key(self.auth_cookie):
del resp.cookies[self.auth_cookie]
# Redirect if desired.
url = self.getUnauthorizedURL()
if url is not None:
resp.redirect(url, lock=1)
# We don't need to raise an exception.
return
# Fall through to the standard _unauthorized() call.
resp._unauthorized()
security.declarePublic('getUnauthorizedURL')
def getUnauthorizedURL(self):
'''
Redirects to the login page.
'''
# XXX: this method violates the rules for tools/utilities:
# it depends on self.REQUEST
req = self.REQUEST
resp = req['RESPONSE']
attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
if attempt == ATTEMPT_NONE:
# An anonymous user was denied access to something.
page_id = self.auto_login_page
retry = ''
elif attempt == ATTEMPT_LOGIN:
# The login attempt failed. Try again.
page_id = self.auto_login_page
retry = '1'
else:
# An authenticated user was denied access to something.
page_id = self.unauth_page
retry = ''
if page_id:
page = self.restrictedTraverse(page_id, None)
if page is not None:
came_from = req.get('came_from', None)
if came_from is None:
came_from = req.get('ACTUAL_URL')
query = req.get('QUERY_STRING')
if query:
# Include the query string in came_from
if not query.startswith('?'):
query = '?' + query
came_from = came_from + query
url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
page.absolute_url(), quote(came_from), retry)
return url
return None
CookieCrumbler.__call__ = __call__
CookieCrumbler._cleanupResponse = _cleanupResponse
CookieCrumbler.unauthorized = unauthorized
CookieCrumbler._unauthorized = _unauthorized
CookieCrumbler.getUnauthorizedURL = getUnauthorizedURL
###
CookieCrumbler.security = security CookieCrumbler.security = security
InitializeClass(CookieCrumbler) InitializeClass(CookieCrumbler)
##############################################################################
#
# Copyright (c) 2001 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Products.CMFCore.DynamicType import DynamicType
def getTypeInfo(self):
""" Get the TypeInformation object specified by the portal type.
"""
# <patch>
tool = getattr(self.getPortalObject(), "portal_types", None)
# </patch>
if tool is None:
return None
return tool.getTypeInfo(self) # Can return None.
DynamicType.getTypeInfo = getTypeInfo
...@@ -265,7 +265,9 @@ allow_module('cStringIO') ...@@ -265,7 +265,9 @@ allow_module('cStringIO')
import cStringIO import cStringIO
allow_type(cStringIO.InputType) allow_type(cStringIO.InputType)
allow_type(cStringIO.OutputType) allow_type(cStringIO.OutputType)
allow_module('io')
import io
allow_type(io.BytesIO)
ModuleSecurityInfo('cgi').declarePublic('escape', 'parse_header') ModuleSecurityInfo('cgi').declarePublic('escape', 'parse_header')
allow_module('datetime') allow_module('datetime')
......
...@@ -951,4 +951,17 @@ def canDoActionFor(self, ob, action, wf_id=None, guard_kw={}): ...@@ -951,4 +951,17 @@ def canDoActionFor(self, ob, action, wf_id=None, guard_kw={}):
WorkflowTool.canDoActionFor = canDoActionFor WorkflowTool.canDoActionFor = canDoActionFor
security.declarePrivate('_listTypeInfo')
def _listTypeInfo(self):
""" List the portal types which are available.
"""
# <patch>
ttool = getattr(self.getPortalObject(), "portal_types", None)
# </patch>
if ttool is not None:
return ttool.listTypeInfo()
return ()
WorkflowTool._listTypeInfo = _listTypeInfo
InitializeClass(WorkflowTool) InitializeClass(WorkflowTool)
...@@ -839,6 +839,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase): ...@@ -839,6 +839,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
PAS._extractUserIds = orig_extractUserIds PAS._extractUserIds = orig_extractUserIds
# Restore security manager # Restore security manager
setSecurityManager(sm) setSecurityManager(sm)
# Restore site removed by closing of request
setSite(self.portal)
# Make sure that the skin cache does not have objects that were # Make sure that the skin cache does not have objects that were
# loaded with the connection used by the requested url. # loaded with the connection used by the requested url.
......
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