Commit dfb15874 authored by Julien Muchembled's avatar Julien Muchembled

Add support for CMF 2.3

See merge request !957
parents 989ad1fd 9f248dfc
......@@ -6899,17 +6899,17 @@ class TestBusinessTemplate(BusinessTemplateMixin):
Updating from:
portal_categories/test_category/modified
portal_categories/test_category/modified/container_in_which_child_is_added
portal_categories/test_category/modified/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified/removed
portal_categories/test_category/modified_category
portal_categories/test_category/modified_category/container_in_which_child_is_added
portal_categories/test_category/modified_category/container_in_which_child_is_added/child_kept
portal_categories/test_category/modified_category/removed
to:
portal_categories/test_category/modified <-- this will be modified
portal_categories/test_category/modified/container_in_which_child_is_added
portal_categories/test_category/modified/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 <-- this will be modified
portal_categories/test_category/modified_category/container_in_which_child_is_added
portal_categories/test_category/modified_category/container_in_which_child_is_added/child_kept
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.
......@@ -6920,7 +6920,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
portal_categories.manage_delObjects(['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='removed')
parent_category.container_in_which_child_is_added.newContent(portal_type='Category', id='child_kept')
......@@ -6945,7 +6945,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
shutil.rmtree(export_dir)
# 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.manage_delObjects(['removed'])
......@@ -6959,7 +6959,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
self.tic()
new_business_template_version_1.install()
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'
)
self.tic()
......@@ -6971,7 +6971,7 @@ class TestBusinessTemplate(BusinessTemplateMixin):
self.tic()
self.assertEqual(
'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):
"""Tests for `template_keep_path_list` feature for the special case of catalog methods
......
......@@ -576,7 +576,7 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
if i.getId() not in ('portal_uidhandler',) and
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.component import queryUtility
from zope.i18n.interfaces import ITranslationDomain
......@@ -594,25 +594,27 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
self.assertEqual(queryUtility(ITranslationDomain, 'ui'), None)
# now let's simulate a site just migrated from Zope 2.8 that's being
# accessed for the first time:
old_site = getSite()
try:
from Products.ERP5 import ERP5Site
if 1: # BBB
setSite()
# Sites from Zope2.8 don't have a site_manager yet.
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
self.assertEqual(queryUtility(ITranslationDomain, 'erp5_ui'), None)
# Now simulate first access. Default behaviour from
# ObjectManager is to raise a ComponentLookupError here:
setSite(self.portal)
self.commit()
self.assertIsNotNone(ERP5Site._missing_tools_registered)
# This should have automatically reconstructed the i18n utility
# registrations:
self.assertEqual(queryUtility(ITranslationDomain, 'erp5_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):
"""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
from Products.ERP5Type.tests.utils import createZODBPythonScript
from Products.ERP5Type.tests.utils import removeZODBPythonScript
from Products.ERP5Type import Permissions
from DateTime import DateTime
class PropertySheetTestCase(ERP5TypeTestCase):
"""Base test case class for property sheets tests.
......@@ -2478,8 +2479,9 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor):
portal = self.getPortalObject()
folder = self.getOrganisationModule()
obj = folder.newContent(portal_type='Organisation')
self.assertNotEquals(obj.getCreationDate(), portal.CreationDate())
self.assertNotEquals(obj.getCreationDate(), folder.getCreationDate())
self.assertIsInstance(portal.creation_date, DateTime)
self.assertLess(portal.creation_date, obj.getCreationDate())
self.assertIsNone(folder.getCreationDate())
def test_copyWithoutModificationRight(self):
"""
......
......@@ -638,9 +638,6 @@ class TestProxyField(ERP5TypeTestCase):
self.container = Folder('container').__of__(self.portal)
self.container.manage_addProduct['ERP5Form'].addERP5Form('Base_viewProxyFieldLibrary', 'Proxys')
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):
form.manage_addField(id_, title, field_type)
......
......@@ -17,6 +17,8 @@ Save, download or return generated PDF Document
# doc_pdf_file pdf content to store
# doc_aggregate_list not applicable (only used for events)
from io import BytesIO
if doc_save:
dms_module = getattr(context, 'document_module', None)
if dms_module is not None:
......@@ -31,7 +33,7 @@ if doc_save:
)
document.edit(
source_reference=''.join([doc_reference, '.pdf']),
file=doc_pdf_file
file=BytesIO(doc_pdf_file)
)
document.setContentType("application/pdf")
......
portal_properties | csv_export
portal_properties | csv_import
\ No newline at end of file
portal_actions | csv_export
portal_actions | csv_import
\ No newline at end of file
......@@ -672,10 +672,9 @@ class TestDocument(TestDocumentMixin):
for document_type in portal.getPortalDocumentTypeList():
module = portal.getDefaultModule(document_type)
obj = module.newContent(portal_type=document_type)
self.assertNotEquals(obj.getCreationDate(),
module.getCreationDate())
self.assertNotEquals(obj.getCreationDate(),
portal.CreationDate())
self.assertIsInstance(portal.creation_date, DateTime)
self.assertLess(portal.creation_date, obj.getCreationDate())
self.assertIsNone(module.getCreationDate())
def test_06_ProcessingStateOfAClonedDocument(self):
"""
......@@ -2865,7 +2864,7 @@ class TestDocumentWithSecurity(TestDocumentMixin):
reference='Foo_001',
title='Foo_OO1')
f = makeFileUpload('Foo_001.odt')
text_document.edit(file=f.read())
text_document.edit(file=f)
f.close()
self.tic()
......
......@@ -186,6 +186,8 @@ class TestWebDavSupport(ERP5TypeTestCase):
"""
iso_text_content = text_content.decode('utf-8').encode('iso-8859-1')
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),
request_method='PUT',
stdin=StringIO(iso_text_content),
......
......@@ -34,6 +34,7 @@ import random
import tempfile
from xml.dom.minidom import getDOMImplementation
from App.config import getConfiguration
from Products.CMFCore.ActionsTool import ActionsTool
from Products.ERP5.Document.BusinessTemplate import \
BusinessTemplateMissingDependency
......@@ -51,7 +52,6 @@ class TestTemplateTool(ERP5TypeTestCase):
return ('erp5_core_proxy_field_legacy',
'erp5_full_text_mroonga_catalog',
'erp5_base',
'erp5_csv_style',
'erp5_crm',
'erp5_forge')
......@@ -140,7 +140,7 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(test_web.getTitle(), 'test_web')
self.assertEqual(len(test_web.getRevision()), 28)
def test_updateBusinessTemplateFromUrl_simple(self):
def test_00_updateBusinessTemplateFromUrl_simple(self):
"""
Test updateBusinessTemplateFromUrl method
......@@ -148,13 +148,42 @@ class TestTemplateTool(ERP5TypeTestCase):
the new bt5 is not installed, only imported.
"""
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
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')
# fake different revision
old_bt.setRevision('')
url = 'https://svn.erp5.org/repos/public/erp5/trunk/bt5/erp5_csv_style'
template_tool.updateBusinessTemplateFromUrl(url)
new_bt = template_tool.getInstalledBusinessTemplate('erp5_csv_style')
# Break the properties tool
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.assertEqual('erp5_csv_style', new_bt.getTitle())
......
......@@ -1869,6 +1869,7 @@ class ToolTemplateItem(PathTemplateItem):
def install(self, context, trashbin, **kw):
""" When we install a tool that is a type provider not
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)
portal = context.getPortalObject()
......@@ -1879,6 +1880,8 @@ class ToolTemplateItem(PathTemplateItem):
type_container_id not in types_tool.type_provider_list):
types_tool.type_provider_list = tuple(types_tool.type_provider_list) + \
(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):
""" When we uninstall a tool, unregister it from the type provider. """
......@@ -2121,7 +2124,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
update_dict = kw.get('object_to_update')
force = kw.get('force')
portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins')
skin_tool = portal.portal_skins
for skin_folder_id in self._objects.keys():
......@@ -2163,7 +2166,7 @@ class RegisteredSkinSelectionTemplateItem(BaseTemplateItem):
def uninstall(self, context, **kw):
portal = context.getPortalObject()
skin_tool = getToolByName(portal, 'portal_skins')
skin_tool = portal.portal_skins
object_path = kw.get('object_path')
for skin_folder_id in (object_path,) if object_path else self._objects:
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
from erp5.component.document.Document import VALID_IMAGE_FORMAT_LIST
from erp5.component.document.Document import ConversionError
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 cStringIO import StringIO
def _unpackData(data):
"""
......@@ -46,7 +47,7 @@ def _unpackData(data):
_MARKER = object()
class File(Document, CMFFile):
class File(Document, OFS_File):
"""
A File can contain raw data which can be uploaded and downloaded.
It is the root class of Image, OOoDocument (ERP5OOo product),
......@@ -110,8 +111,14 @@ class File(Document, CMFFile):
filename = kw.get('filename')
if filename:
self._setFilename(filename)
if self._isNotEmpty(file_object):
self._setFile(file_object, precondition=precondition)
if file_object is not None:
# 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)
security.declareProtected( Permissions.ModifyPortalContent, 'edit' )
......@@ -138,11 +145,18 @@ class File(Document, CMFFile):
return None
def _setFile(self, data, precondition=None):
if data is not None and self.hasData() and \
str(data.read()) == str(self.getData()):
# Same data as previous, no need to change it's content
if data is None:
return
if self.hasData():
if str(data.read()) == str(self.getData()):
# Same data as previous, no need to change its content
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')
def setFile(self, data, precondition=None):
......@@ -176,11 +190,16 @@ class File(Document, CMFFile):
return str(data)
# 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',
'manage_FTPlist')
manage_FTPlist = CMFFile.manage_FTPlist
manage_FTPstat = CMFFile.manage_FTPstat
manage_FTPlist = OFS_File.manage_FTPlist
manage_FTPstat = OFS_File.manage_FTPstat
security.declareProtected(Permissions.AccessContentsInformation, 'getMimeTypeAndContent')
def getMimeTypeAndContent(self):
......
......@@ -23,6 +23,7 @@ from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type import Permissions
from Products.CMFCore.PortalContent import ResourceLockedError
from zExceptions import Forbidden
from cStringIO import StringIO
security = ModuleSecurityInfo(__name__)
......@@ -76,7 +77,7 @@ class TextContent:
headers = self.parseHeadersFromText(body)
content_type = REQUEST.get_header('Content-Type', '')
headers.setdefault('content_type', content_type)
headers['file'] = body
headers['file'] = StringIO(body)
self._edit(**headers)
except ResourceLockedError:
transaction.abort()
......
......@@ -85,7 +85,6 @@ print reindex(
'portal_classes',
'portal_preferences',
'portal_simulation',
'portal_uidhandler',
) and
'inventory' not in x.id
],
......
"Modified version for ERP5 to append the default action (/view) in the URL."
from Products.CMFCore.utils import getToolByName
ptool = getToolByName(script, 'portal_properties')
utool = getToolByName(script, 'portal_url')
portal = context.getPortalObject()
utool = portal.portal_url
portal_url = utool()
result = []
param = int(context.REQUEST.get('ignore_layout', 0)) and '?ignore_layout:int=1' or ''
param = '?ignore_layout:int=1' if int(portal.REQUEST.get('ignore_layout', 0)) else ''
if include_root:
result.append( { 'id' : 'root'
, 'title' : ptool.title()
, 'url' : '%s/view%s' % (portal_url, param)
}
)
relative = utool.getRelativeContentPath(context)
portal = utool.getPortalObject()
result = [{
'id' : 'root',
'title' : portal.title,
'url' : '%s/view%s' % (portal_url, param),
}]
else:
result = []
obj = portal
now = []
for name in relative:
for name in utool.getRelativeContentPath(context):
obj = obj.restrictedTraverse(name)
now.append(name)
title = (
getattr(obj, "getCompactTranslatedTitle", lambda: None)() or
obj.getTitle() or obj.getId()
)
if not name == 'talkback':
if name != 'talkback':
result.append( { 'id' : name
, 'title' : title
, 'url' : '%s/%s/view%s' % (portal_url, '/'.join(now), param)
......
......@@ -69,7 +69,6 @@ class TestFormPrintoutAsODG(TestFormPrintoutMixin):
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
if custom._getOb('Foo_getODGStyleSheet', None) is None:
addStyleSheet(id='Foo_getODGStyleSheet', file=foo_file, title='',
precondition='',
content_type='application/vnd.oasis.opendocument.graphics')
erp5OOo = custom.manage_addProduct['ERP5OOo']
......
......@@ -84,23 +84,22 @@ class TestFormPrintoutAsODT(TestFormPrintoutMixin):
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
if custom._getOb('Foo_getODTStyleSheet', None) is None:
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:
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:
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:
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:
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:
addStyleSheet(id='Foo_getVariableODTStyleSheet',
file=variable_file_object, title='',
precondition='',
content_type='application/vnd.oasis.opendocument.text')
erp5OOo = custom.manage_addProduct['ERP5OOo']
......
......@@ -66,7 +66,7 @@ class TestOoodResponse(ERP5TypeTestCase):
custom = portal_skins.custom
addStyleSheet = custom.manage_addProduct['OFSP'].manage_addFile
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(id='ERP5Site_viewNothingAsOdt', title='')
portal_skins.changeSkin(skinname=None)
......
......@@ -76,10 +76,10 @@ class TestOooDynamicStyle(ERP5TypeTestCase):
addStyleSheet = self.getPortal().manage_addProduct['OFSP'].manage_addFile
if getattr(self.getPortal(), 'Test_getODTStyleSheet_en', None) is None:
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:
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:
script_body = """
current_language = context.Localizer.get_selected_language()
......
......@@ -81,7 +81,6 @@ from CopySupport import CopyContainer, CopyError,\
from Errors import DeferredCatalogError, UnsupportedWorkflowMethod
from Products.CMFActivity.ActiveObject import ActiveObject
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.ConsistencyMessage import ConsistencyMessage
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod, super_user
......@@ -3229,9 +3228,8 @@ class Base(
if history and 'time' in history[0])
except ValueError:
pass
if getattr(aq_base(self), 'CreationDate', None) is not None:
return asDate(self.CreationDate())
return None # JPS-XXX - try to find a way to return a creation date instead of None
if getattr(aq_base(self), 'creation_date', None):
return self.creation_date.toZone(DateTime().timezone())
security.declareProtected(Permissions.AccessContentsInformation, 'getModificationDate')
def getModificationDate(self):
......@@ -3260,7 +3258,13 @@ class Base(
# Return a copy of history time, to prevent modification
return DateTime(max_date)
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
security.declareProtected(Permissions.AccessContentsInformation, 'getApplicableLayout')
......
......@@ -34,6 +34,7 @@ from Products.ERP5Type.patches import DCWorkflow
from Products.ERP5Type.patches import Worklists
from Products.ERP5Type.patches import BTreeFolder2
from Products.ERP5Type.patches import WorkflowTool
from Products.ERP5Type.patches import DynamicType
from Products.ERP5Type.patches import XMLExportImport
from Products.ERP5Type.patches import ppml
from Products.ERP5Type.patches import Expression
......
......@@ -189,3 +189,18 @@ import zExceptions
ModuleSecurityInfo('zExceptions').declarePublic(*filter(
lambda x: Exception in getattr(getattr(zExceptions, x), '__mro__', ()),
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):
except AttributeError:
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:
portal.migrateToPortalTypeClass()
portal.portal_skins.changeSkin(None)
......
......@@ -141,9 +141,7 @@ def CMFCoreSkinnableSkinnableObjectManager_changeSkin(self, skinname, REQUEST=No
Patched not to call getSkin.
'''
if skinname is None:
sfn = self.getSkinsFolderName()
if sfn is not None:
sf = getattr(self, sfn, None)
sf = getattr(self, "portal_skins", None)
if sf is not None:
skinname = sf.getDefaultSkin()
tid = get_ident()
......
......@@ -13,13 +13,14 @@
##############################################################################
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
def _setCacheHeaders(obj, extra_context):
"""Set cache headers according to cache policy manager for the obj."""
REQUEST = getattr(obj, 'REQUEST', None)
if REQUEST is not None:
call_count = getattr(REQUEST, SUBTEMPLATE, 1) - 1
setattr(REQUEST, SUBTEMPLATE, call_count)
......@@ -30,7 +31,7 @@ def _setCacheHeaders(obj, extra_context):
delattr(REQUEST, SUBTEMPLATE)
content = aq_parent(obj)
manager = getToolByName(obj, 'caching_policy_manager', None)
manager = queryUtility(ICachingPolicyManager)
if manager is None:
return
......
......@@ -30,6 +30,7 @@ from App.class_init import InitializeClass
from Products.CMFCore.CookieCrumbler import CookieCrumbler
from Products.CMFCore.CookieCrumbler import CookieCrumblerDisabled
from urllib import quote, unquote
from zExceptions import Redirect
from ZPublisher.HTTPRequest import HTTPRequest
ATTEMPT_NONE = 0 # No attempt at authentication
......@@ -47,6 +48,10 @@ class PatchedCookieCrumbler(CookieCrumbler):
security = ClassSecurityInfo()
CookieCrumbler.auto_login_page = 'login_form'
CookieCrumbler.unauth_page = ''
CookieCrumbler.logout_page = 'logged_out'
def getLoginURL(self):
'''
Redirects to the login page.
......@@ -176,14 +181,175 @@ def modifyRequest(self, req, resp):
CookieCrumbler.modifyRequest = modifyRequest
def credentialsChanged(self, user, name, pw):
ac = standard_b64encode('%s:%s' % (name, pw))
method = self.getCookieMethod( 'setAuthCookie'
, self.defaultSetAuthCookie )
resp = self.REQUEST['RESPONSE']
method( resp, self.auth_cookie, quote( ac ) )
def credentialsChanged(self, user, name, pw, request=None):
"""
Updates cookie credentials if user details are changed.
"""
if request is None:
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
# 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
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')
import cStringIO
allow_type(cStringIO.InputType)
allow_type(cStringIO.OutputType)
allow_module('io')
import io
allow_type(io.BytesIO)
ModuleSecurityInfo('cgi').declarePublic('escape', 'parse_header')
allow_module('datetime')
......
......@@ -951,4 +951,17 @@ def canDoActionFor(self, ob, action, wf_id=None, guard_kw={}):
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)
......@@ -839,6 +839,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
PAS._extractUserIds = orig_extractUserIds
# Restore security manager
setSecurityManager(sm)
# Restore site removed by closing of request
setSite(self.portal)
# Make sure that the skin cache does not have objects that were
# 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