Commit dfb15874 authored by Julien Muchembled's avatar Julien Muchembled

Add support for CMF 2.3

See merge request nexedi/erp5!957
parents 989ad1fd 9f248dfc
...@@ -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]
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
Portal class Portal class
""" """
from DateTime import DateTime
from six.moves import map
import thread, threading import thread, threading
from weakref import ref as weakref from weakref import ref as weakref
from OFS.Application import Application, AppInitializer from OFS.Application import Application, AppInitializer
...@@ -25,7 +27,7 @@ from Products.SiteErrorLog.SiteErrorLog import manage_addErrorLog ...@@ -25,7 +27,7 @@ from Products.SiteErrorLog.SiteErrorLog import manage_addErrorLog
from ZPublisher import BeforeTraverse from ZPublisher import BeforeTraverse
from ZPublisher.BaseRequest import RequestContainer from ZPublisher.BaseRequest import RequestContainer
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.CMFDefault.Portal import CMFSite from Products.CMFCore.PortalObject import PortalObjectBase
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Core.Folder import FolderMixIn from Products.ERP5Type.Core.Folder import FolderMixIn
from Acquisition import aq_base from Acquisition import aq_base
...@@ -36,7 +38,8 @@ from Products.ERP5Type.ERP5Type import ERP5TypeInformation ...@@ -36,7 +38,8 @@ from Products.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.ERP5Type.patches.CMFCoreSkinnable import SKINDATA, skinResolve from Products.ERP5Type.patches.CMFCoreSkinnable import SKINDATA, skinResolve
from Products.CMFActivity.Errors import ActivityPendingError from Products.CMFActivity.Errors import ActivityPendingError
import ERP5Defaults import ERP5Defaults
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable from Products.ERP5Type.TransactionalVariable import \
getTransactionalVariable, TransactionalResource
from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules
from Products.ERP5Type.mixin.response_header_generator import ResponseHeaderGenerator from Products.ERP5Type.mixin.response_header_generator import ResponseHeaderGenerator
...@@ -227,8 +230,9 @@ class _site(threading.local): ...@@ -227,8 +230,9 @@ class _site(threading.local):
getSite, setSite = _site() getSite, setSite = _site()
_missing_tools_registered = None
class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): class ERP5Site(ResponseHeaderGenerator, FolderMixIn, PortalObjectBase, CacheCookieMixin):
""" """
The *only* function this class should have is to help in the setup The *only* function this class should have is to help in the setup
of a new ERP5. It should not assist in the functionality at all. of a new ERP5. It should not assist in the functionality at all.
...@@ -269,6 +273,15 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -269,6 +273,15 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def __init__(self, id):
PortalObjectBase.__init__(self, id)
self.creation_date = DateTime()
security.declarePrivate('reindexObject')
def reindexObject(self, idxs=[]):
"""from Products.CMFDefault.Portal"""
pass
security.declarePublic('isSubtreeIndexable') security.declarePublic('isSubtreeIndexable')
def isSubtreeIndexable(self): def isSubtreeIndexable(self):
""" """
...@@ -328,7 +341,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -328,7 +341,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
from Products.Localizer.MessageCatalog import ( from Products.Localizer.MessageCatalog import (
message_catalog_alias_sources message_catalog_alias_sources
) )
sm = self.getSiteManager() sm = self._components
for message_catalog in self.Localizer.objectValues(): for message_catalog in self.Localizer.objectValues():
sm.registerUtility(message_catalog, sm.registerUtility(message_catalog,
provided=ITranslationDomain, provided=ITranslationDomain,
...@@ -338,21 +351,31 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -338,21 +351,31 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
provided=ITranslationDomain, provided=ITranslationDomain,
name=alias) name=alias)
def _doInitialSiteManagerMigration(self): _registry_tool_id_list = 'caching_policy_manager',
self._createInitialSiteManager() def _registerMissingTools(self):
# Now that we have a sitemanager, se can do things that require tool_id_list = ("portal_skins", "portal_types", "portal_membership",
# one. Including setting up ZTK style utilities and adapters. We "portal_url", "portal_workflow")
# can even call setSite(self), as long as we roll back that later, if (None in map(self.get, tool_id_list) or not
# since we are actually in the middle of a setSite() call. TransactionalResource.registerOnce(__name__, 'site_manager', self.id)):
from zope.site.hooks import getSite, setSite return
old_site = getSite() self._registerTools(tool_id_list + self._registry_tool_id_list)
try: def markRegistered(txn):
setSite(self) global _missing_tools_registered
# setSite(self) is not really necessary for the migration below, but _missing_tools_registered = self.id
# could be needed by other migrations to be added here. TransactionalResource(tpc_finish=markRegistered)
self._doTranslationDomainRegistration()
finally: def _registerTools(self, tool_id_list):
setSite(old_site) from Products.CMFCore import interfaces, utils
sm = self._components
for tool_id in tool_id_list:
tool = self.get(tool_id, None)
if tool:
tool_interface = utils._tool_interface_registry.get(tool_id)
if tool_interface is not None:
# Note: already registered tools will be either:
# - updated
# - registered again after being unregistered
sm.registerUtility(aq_base(tool), tool_interface)
# backward compatibility auto-migration # backward compatibility auto-migration
def getSiteManager(self): def getSiteManager(self):
...@@ -369,14 +392,29 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -369,14 +392,29 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
# OFS.ObjectManager.ObjectManager.getSiteManager(), and is exactly # OFS.ObjectManager.ObjectManager.getSiteManager(), and is exactly
# as cheap as it is on the case that self._components is already # as cheap as it is on the case that self._components is already
# set. # set.
if self.id == _missing_tools_registered:
return self._components # fast path
_components = self._components _components = self._components
if _components is not None: if _components is None:
# only create _components
self._createInitialSiteManager()
_components = self._components
# Now that we have a sitemanager, se can do things that require
# one. Including setting up ZTK style utilities and adapters. We
# can even call setSite(self), as long as we roll back that later,
# since we are actually in the middle of a setSite() call.
from zope.site.hooks import getSite, setSite
old_site = getSite()
try:
setSite(self)
self._doTranslationDomainRegistration()
self._registerMissingTools()
finally:
setSite(old_site)
else:
self._registerMissingTools()
return _components return _components
# This method below can take as (reasonably) long as it pleases
# since it will not be run ever again
self._doInitialSiteManagerMigration()
assert self._components is not None, 'Migration Failed!'
return self._components
security.declareProtected(Permissions.View, 'view') security.declareProtected(Permissions.View, 'view')
def view(self): def view(self):
...@@ -387,7 +425,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -387,7 +425,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
return self.index_html() return self.index_html()
def __of__(self, parent): def __of__(self, parent):
self = CMFSite.__of__(self, parent) self = PortalObjectBase.__of__(self, parent)
# Use a transactional variable for performance reason, # Use a transactional variable for performance reason,
# since ERP5Site.__of__ is called quite often. # since ERP5Site.__of__ is called quite often.
tv = getTransactionalVariable() tv = getTransactionalVariable()
...@@ -471,7 +509,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -471,7 +509,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
path_item_list=path_item_list, path_item_list=path_item_list,
new_id=new_id) new_id=new_id)
# Rename the object # Rename the object
return CMFSite.manage_renameObject(self, id=id, new_id=new_id, return PortalObjectBase.manage_renameObject(self, id=id, new_id=new_id,
REQUEST=REQUEST) REQUEST=REQUEST)
...@@ -589,7 +627,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -589,7 +627,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
# _getProperty is missing, but since there are no protected properties # _getProperty is missing, but since there are no protected properties
# on an ERP5 Site, we can just use getProperty instead. # on an ERP5 Site, we can just use getProperty instead.
_getProperty = CMFSite.getProperty _getProperty = PortalObjectBase.getProperty
security.declareProtected(Permissions.AccessContentsInformation, 'getUid') security.declareProtected(Permissions.AccessContentsInformation, 'getUid')
def getUid(self): def getUid(self):
...@@ -703,7 +741,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin): ...@@ -703,7 +741,7 @@ class ERP5Site(ResponseHeaderGenerator, FolderMixIn, CMFSite, CacheCookieMixin):
email_from_address, email_from_name, email_from_address, email_from_name,
validate_email validate_email
): ):
CMFSite.setupDefaultProperties(self, p, title, description, PortalObjectBase.setupDefaultProperties(self, p, title, description,
email_from_address, email_from_name, email_from_address, email_from_name,
validate_email) validate_email)
...@@ -1863,7 +1901,7 @@ factory_type_information = () # No original CMF portal_types installed by defaul ...@@ -1863,7 +1901,7 @@ factory_type_information = () # No original CMF portal_types installed by defaul
class PortalGenerator: class PortalGenerator:
klass = CMFSite klass = PortalObjectBase
def setupTools(self, p): def setupTools(self, p):
"""Set up initial tools""" """Set up initial tools"""
...@@ -1872,29 +1910,13 @@ class PortalGenerator: ...@@ -1872,29 +1910,13 @@ class PortalGenerator:
addCMFCoreTool('CMF Actions Tool', None) addCMFCoreTool('CMF Actions Tool', None)
addCMFCoreTool('CMF Catalog', None) addCMFCoreTool('CMF Catalog', None)
addCMFCoreTool('CMF Member Data Tool', None) addCMFCoreTool('CMF Member Data Tool', None)
addCMFCoreTool('CMF Membership Tool', None)
addCMFCoreTool('CMF Registration Tool', None)
addCMFCoreTool('CMF Skins Tool', None) addCMFCoreTool('CMF Skins Tool', None)
addCMFCoreTool('CMF Undo Tool', None) addCMFCoreTool('CMF Undo Tool', None)
addCMFCoreTool('CMF URL Tool', None) addCMFCoreTool('CMF URL Tool', None)
addCMFCoreTool('CMF Workflow Tool', None) addCMFCoreTool('CMF Workflow Tool', None)
addCMFDefaultTool = p.manage_addProduct['CMFDefault'].manage_addTool
addCMFDefaultTool('Default Discussion Tool', None)
addCMFDefaultTool('Default Membership Tool', None)
addCMFDefaultTool('Default Registration Tool', None)
addCMFDefaultTool('Default Properties Tool', None)
addCMFDefaultTool('Default Metadata Tool', None)
addCMFDefaultTool('Default Syndication Tool', None)
# try to install CMFUid without raising exceptions if not available
try:
addCMFUidTool = p.manage_addProduct['CMFUid'].manage_addTool
except AttributeError:
pass
else:
addCMFUidTool('Unique Id Annotation Tool', None)
addCMFUidTool('Unique Id Generator Tool', None)
addCMFUidTool('Unique Id Handler Tool', None)
def setupMailHost(self, p): def setupMailHost(self, p):
p.manage_addProduct['MailHost'].manage_addMailHost( p.manage_addProduct['MailHost'].manage_addMailHost(
'MailHost', smtp_host='localhost') 'MailHost', smtp_host='localhost')
...@@ -2194,8 +2216,6 @@ class ERP5Generator(PortalGenerator): ...@@ -2194,8 +2216,6 @@ class ERP5Generator(PortalGenerator):
'manage_members')) 'manage_members'))
# actions tool # actions tool
removeActionsFromTool(p.portal_actions, ('folderContents',)) removeActionsFromTool(p.portal_actions, ('folderContents',))
# properties tool
removeActionsFromTool(p.portal_properties, ('configPortal',))
# remove unused action providers # remove unused action providers
for i in ('portal_registration', 'portal_discussion', 'portal_syndication'): for i in ('portal_registration', 'portal_discussion', 'portal_syndication'):
p.portal_actions.deleteActionProvider(i) p.portal_actions.deleteActionProvider(i)
...@@ -2206,35 +2226,14 @@ class ERP5Generator(PortalGenerator): ...@@ -2206,35 +2226,14 @@ class ERP5Generator(PortalGenerator):
""" """
pass pass
# this lists only the skin layers of Products.CMFDefault we are actually
# interested in.
CMFDEFAULT_FOLDER_LIST = ['Images']
def addCMFDefaultDirectoryViews(self, p):
"""Semi-manually create DirectoryViews since CMFDefault 2.X no longer
registers the "skins" directory, only its subdirectories, making it
unusable with Products.CMFCore.DirectoryView.addDirectoryViews."""
from Products.CMFCore.DirectoryView import createDirectoryView, _generateKey
import Products.CMFDefault
ps = p.portal_skins
# get the layer directories actually present
for cmfdefault_skin_layer in self.CMFDEFAULT_FOLDER_LIST:
reg_key = _generateKey(Products.CMFDefault.__name__,
'skins/' + cmfdefault_skin_layer)
createDirectoryView(ps, reg_key)
def setupDefaultSkins(self, p): def setupDefaultSkins(self, p):
ps = p.portal_skins ps = p.portal_skins
self.addCMFDefaultDirectoryViews(p)
ps.manage_addProduct['OFSP'].manage_addFolder(id='external_method') ps.manage_addProduct['OFSP'].manage_addFolder(id='external_method')
ps.manage_addProduct['OFSP'].manage_addFolder(id='custom') ps.manage_addProduct['OFSP'].manage_addFolder(id='custom')
# Set the 'custom' layer a high priority, so it remains the first # Set the 'custom' layer a high priority, so it remains the first
# layer when installing new business templates. # layer when installing new business templates.
ps['custom'].manage_addProperty("business_template_skin_layer_priority", 100.0, "float") ps['custom'].manage_addProperty("business_template_skin_layer_priority", 100.0, "float")
skin_folder_list = [ 'custom' skin_folders = ', '.join(('custom', 'external_method'))
, 'external_method'
] + self.CMFDEFAULT_FOLDER_LIST
skin_folders = ', '.join(skin_folder_list)
ps.addSkinSelection( 'View' ps.addSkinSelection( 'View'
, skin_folders , skin_folders
, make_default = 1 , make_default = 1
......
...@@ -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