diff --git a/bt5/erp5_corporate_identity/SkinTemplateItem/portal_skins/erp5_corporate_identity/WebPage_finishPdfCreation.py b/bt5/erp5_corporate_identity/SkinTemplateItem/portal_skins/erp5_corporate_identity/WebPage_finishPdfCreation.py index 0fdc074e8cb0aaf10a7fac25c0ee6566e7552526..0fa3ce91d22bcd4bc2e638ea40e22e18572f5c34 100644 --- a/bt5/erp5_corporate_identity/SkinTemplateItem/portal_skins/erp5_corporate_identity/WebPage_finishPdfCreation.py +++ b/bt5/erp5_corporate_identity/SkinTemplateItem/portal_skins/erp5_corporate_identity/WebPage_finishPdfCreation.py @@ -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") diff --git a/product/ERP5/Document/File.py b/product/ERP5/Document/File.py index 10cb3668d63f4de1f04888ae8b448a9a1ff9e14b..df829d234afeb2eb1e6f47360f582a987f225f89 100644 --- a/product/ERP5/Document/File.py +++ b/product/ERP5/Document/File.py @@ -27,6 +27,7 @@ # ############################################################################## +from DateTime import DateTime from AccessControl import ClassSecurityInfo from Products.ERP5Type.Base import WorkflowMethod from Products.ERP5Type import Permissions, PropertySheet @@ -34,8 +35,7 @@ from Products.ERP5.Document.Document import Document, VALID_TEXT_FORMAT_LIST from Products.ERP5.Document.Document import VALID_IMAGE_FORMAT_LIST from Products.ERP5.Document.Document import ConversionError from Products.ERP5Type.Base import Base, removeIContentishInterface -from Products.CMFDefault.File import File as CMFFile -from OFS.Image import Pdata +from OFS.Image import File as OFS_File from cStringIO import StringIO from Products.ERP5Type.Utils import deprecated @@ -48,7 +48,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), @@ -112,8 +112,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, precondition=precondition) Base._edit(self, **kw) security.declareProtected( Permissions.ModifyPortalContent, 'edit' ) @@ -140,11 +146,17 @@ 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 not None and \ + str(data.read()) == (self.hasData() and str(self.getData())): + # Same data as previous, no need to change its content return - CMFFile._edit(self, precondition=precondition, file=data) + + # from Products.CMFDefault.File + if precondition: self.precondition = precondition + elif self.precondition: del self.precondition + if data is not None and data.tell(): + data.seek(0) + self.manage_upload(data) security.declareProtected(Permissions.ModifyPortalContent,'setFile') def setFile(self, data, precondition=None): @@ -192,11 +204,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): diff --git a/product/ERP5/ERP5Site.py b/product/ERP5/ERP5Site.py index 45d7697a477ec8b8fdba64d9cab8aeb0566c75bb..0648ed29979f32af15ae907f61fb471334f9e8b4 100644 --- a/product/ERP5/ERP5Site.py +++ b/product/ERP5/ERP5Site.py @@ -15,6 +15,7 @@ Portal class """ +from DateTime import DateTime from six.moves import map import threading from weakref import ref as weakref @@ -26,7 +27,7 @@ from Products.SiteErrorLog.SiteErrorLog import manage_addErrorLog from ZPublisher import BeforeTraverse from ZPublisher.BaseRequest import RequestContainer from AccessControl import ClassSecurityInfo -from Products.CMFDefault.Portal import CMFSite +from Products.CMFCore.PortalObject import PortalObjectBase from Products.ERP5Type import Permissions from Products.ERP5Type.Core.Folder import FolderMixIn from Acquisition import aq_base @@ -230,7 +231,7 @@ getSite, setSite = _site() _missing_tools_registered = None -class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): +class ERP5Site(FolderMixIn, PortalObjectBase, CacheCookieMixin): """ 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. @@ -271,6 +272,15 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): security = ClassSecurityInfo() 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') def isSubtreeIndexable(self): """ @@ -409,7 +419,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): return self.index_html() def __of__(self, parent): - self = CMFSite.__of__(self, parent) + self = PortalObjectBase.__of__(self, parent) # Use a transactional variable for performance reason, # since ERP5Site.__of__ is called quite often. tv = getTransactionalVariable() @@ -479,7 +489,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): path_item_list=path_item_list, new_id=new_id) # 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) @@ -597,7 +607,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): # _getProperty is missing, but since there are no protected properties # on an ERP5 Site, we can just use getProperty instead. - _getProperty = CMFSite.getProperty + _getProperty = PortalObjectBase.getProperty security.declareProtected(Permissions.AccessContentsInformation, 'getUid') def getUid(self): @@ -711,7 +721,7 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin): email_from_address, email_from_name, validate_email ): - CMFSite.setupDefaultProperties(self, p, title, description, + PortalObjectBase.setupDefaultProperties(self, p, title, description, email_from_address, email_from_name, validate_email) @@ -1862,7 +1872,7 @@ factory_type_information = () # No original CMF portal_types installed by defaul class PortalGenerator: - klass = CMFSite + klass = PortalObjectBase def setupTools(self, p): """Set up initial tools""" diff --git a/product/ERP5OOo/tests/testDms.py b/product/ERP5OOo/tests/testDms.py index afde50399761f418ad99de234832e4a86f822553..4398cce99eaccfa99b8195654caa478b8481ecdb 100644 --- a/product/ERP5OOo/tests/testDms.py +++ b/product/ERP5OOo/tests/testDms.py @@ -680,10 +680,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): """ @@ -2884,7 +2883,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() diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py index 109a5c2aaefb0d72d1ade274aa63a77c60b00404..efbcdbf830c6b5245c7635a74699c345bbb3ffa9 100644 --- a/product/ERP5Type/Base.py +++ b/product/ERP5Type/Base.py @@ -83,7 +83,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 @@ -3226,9 +3225,8 @@ class Base( CopyContainer, if history) 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): @@ -3257,7 +3255,13 @@ class Base( CopyContainer, # 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') diff --git a/product/ERP5Type/WebDAVSupport.py b/product/ERP5Type/WebDAVSupport.py index 5d5be52a4718001aea75bdcc7b8870990adf511e..3b0133f592e2f1326481502bfb1770634a0dc64d 100644 --- a/product/ERP5Type/WebDAVSupport.py +++ b/product/ERP5Type/WebDAVSupport.py @@ -15,6 +15,7 @@ import collections import email +from io import BytesIO import re import transaction from lxml import html @@ -80,7 +81,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'] = BytesIO(body) self._edit(**headers) except ResourceLockedError, msg: transaction.abort() diff --git a/product/ERP5Type/tests/testERP5Type.py b/product/ERP5Type/tests/testERP5Type.py index 5a25527190aca45ceb228ed61b1e2ea95198ac74..3aa3e0d380638db8b068c86e19e528f306f8b32d 100644 --- a/product/ERP5Type/tests/testERP5Type.py +++ b/product/ERP5Type/tests/testERP5Type.py @@ -38,6 +38,7 @@ import transaction from random import randint from unittest import expectedFailure from Acquisition import aq_base +from DateTime import DateTime from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.utils import DummyLocalizer from zLOG import INFO @@ -2590,8 +2591,9 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor): portal = self.getPortalObject() folder = self.getOrganisationModule() object = folder.newContent(portal_type='Organisation') - self.assertNotEquals(object.getCreationDate(), portal.CreationDate()) - self.assertNotEquals(object.getCreationDate(), folder.getCreationDate()) + self.assertIsInstance(portal.creation_date, DateTime) + self.assertLess(portal.creation_date, object.getCreationDate()) + self.assertIsNone(folder.getCreationDate()) def test_copyWithoutModificationRight(self): """