diff --git a/integrator/README.txt b/integrator/README.txt index 109aedd..da189aa 100644 --- a/integrator/README.txt +++ b/integrator/README.txt @@ -214,9 +214,15 @@ Uploading Resources with HTTP PUT Requests Extracting Document Properties from MS Office Files =================================================== + >>> import shutil >>> from loops.resource import Resource >>> tOfficeFile = concepts['officefile'] >>> path = os.path.join(dataDir, 'office') + >>> fn = os.path.join(path, 'example.docx') + >>> shutil.copy(fn + '.sav', fn) + >>> os.path.getsize(fn) + 195808L + >>> officeFile = addAndConfigureObject(resources, Resource, 'test.docx', ... title=u'Example Word File', resourceType=tOfficeFile, ... storageParams=dict(subdirectory=path)) @@ -225,7 +231,7 @@ Extracting Document Properties from MS Office Files >>> content = aOfficeFile.data >>> len(content) - 195808 + 192925 Fin de partie diff --git a/integrator/office/base.py b/integrator/office/base.py index b3ba4d5..ed39653 100644 --- a/integrator/office/base.py +++ b/integrator/office/base.py @@ -22,8 +22,9 @@ Resource adapter(s) for MS Office files. $Id$ """ -from xml.dom.minidom import parseString +from lxml import etree from zipfile import ZipFile +import shutil from zope.cachedescriptors.property import Lazy from zope import component from zope.component import adapts @@ -36,6 +37,7 @@ from loops.integrator.interfaces import IOfficeFile from loops.interfaces import IResource, IExternalFile from loops.resource import ExternalFileAdapter from loops.type import TypeInterfaceSourceList +from loops.versioning.interfaces import IVersionable TypeInterfaceSourceList.typeInterfaces += (IOfficeFile,) @@ -48,7 +50,8 @@ class OfficeFile(ExternalFileAdapter): implements(IOfficeFile) - propertyMap = dict(version=u'Revision:') + propertyMap = {u'Revision:': 'version'} + propFileName = 'docProps/custom.xml' def setExternalAddress(self, addr): super(OfficeFile, self).setExternalAddress(addr) @@ -60,13 +63,46 @@ class OfficeFile(ExternalFileAdapter): storage = component.getUtility(IExternalStorage, name=self.storageName) subDir = self.storageParams.get('subdirectory') fn = storage.getDir(self.externalAddress, subDir) + # TODO: check if suitable file type (.docx, .xlsm) # open ZIP file, process properties, set version property in file - zf = ZipFile(fn, 'a') + zf = ZipFile(fn, 'r') #print '***', zf.namelist() - propsXml = zf.read('docProps/custom.xml') - dom = parseString(propsXml) - props = dom.getElementsByTagName('property') - for p in props: - pass - #print '***', p.getAttribute('name'), p.childNodes[0].childNodes[0].data + propsXml = zf.read(self.propFileName) + dom = etree.fromstring(propsXml) + changed = False + docVersion = None + version = IVersionable(self.context).versionId + strType = ('{http://schemas.openxmlformats.org/' + 'officeDocument/2006/docPropsVTypes}lpwstr') + attributes = {} + for p in dom: + name = p.attrib.get('name') + value = p[0].text + #print '***', name, value, p[0].tag + attr = self.propertyMap.get(name) + if attr == 'version': + docVersion = value + if docVersion and docVersion != version: + # update XML + p[0] = etree.Element(strType) + p[0].text = version + changed = True + elif attr is not None: + attributes[attr] = value zf.close() + if changed: + newFn = fn + '.new' + zf = ZipFile(fn, 'r') + newZf = ZipFile(newFn, 'w') + for info in zf.infolist(): + name = info.filename + if name != self.propFileName: + newZf.writestr(info, zf.read(name)) + newZf.writestr(self.propFileName, etree.tostring(dom)) + newZf.close() + shutil.move(newFn, fn) + self.update(attributes) + + def update(self, attributes): + # to be implemented by subclass + pass diff --git a/integrator/testsetup.py b/integrator/testsetup.py index e09b67d..cd4108c 100644 --- a/integrator/testsetup.py +++ b/integrator/testsetup.py @@ -21,6 +21,7 @@ from loops.integrator.office.base import OfficeFile from loops.knowledge.setup import SetupManager as KnowledgeSetupManager from loops.setup import SetupManager, addAndConfigureObject from loops.tests.setup import TestSite as BaseTestSite +from loops.versioning.versionable import VersionableResource dataDir = os.path.join(os.path.dirname(__file__), 'testdata') @@ -37,6 +38,7 @@ class TestSite(BaseTestSite): component.provideAdapter(FileAdapter, provides=IFile) component.provideAdapter(ExternalFileAdapter, provides=IExternalFile) component.provideAdapter(OfficeFile, provides=IOfficeFile) + component.provideAdapter(VersionableResource) component.provideUtility(fullPathStorage(), IExternalStorage, name='fullpath')