Skip to content

Commit

Permalink
Revert "Removed ZMI export/import feature."
Browse files Browse the repository at this point in the history
This reverts commit d9ce0e2.
  • Loading branch information
pbauer authored and mauritsvanrees committed Feb 4, 2017
1 parent c8b74f5 commit c40714a
Show file tree
Hide file tree
Showing 11 changed files with 1,426 additions and 4 deletions.
2 changes: 0 additions & 2 deletions CHANGES.rst
Expand Up @@ -169,8 +169,6 @@ Restructuring

- Drop `OFS.History` functionality.

- Removed ZMI export/import feature.

- Drop ZopeUndo dependency and move undo management to the control panel.

- Simplify ZMI control panel and globally available management screens.
Expand Down
96 changes: 95 additions & 1 deletion src/OFS/ObjectManager.py
Expand Up @@ -14,6 +14,7 @@
"""

from cgi import escape
from cStringIO import StringIO
from logging import getLogger
import copy
import fnmatch
Expand All @@ -31,6 +32,7 @@
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import delete_objects
from AccessControl.Permissions import ftp_access
from AccessControl.Permissions import import_export_objects
from AccessControl import getSecurityManager
from AccessControl.ZopeSecurityPolicy import getRoles
from Acquisition import aq_base, aq_acquire, aq_parent
Expand Down Expand Up @@ -59,6 +61,10 @@
from OFS.event import ObjectWillBeRemovedEvent
from OFS.Lockable import LockableItem
from OFS.subscribers import compatibilityCall
from OFS.XMLExportImport import importXML
from OFS.XMLExportImport import exportXML
from OFS.XMLExportImport import magic

import collections

if bbb.HAS_ZSERVER:
Expand All @@ -80,8 +86,10 @@
# the name BadRequestException is relied upon by 3rd-party code
BadRequestException = BadRequest

bad_id = re.compile(r'[^a-zA-Z0-9-_~,.$\(\)# @]').search
customImporters={magic: importXML,
}

bad_id=re.compile(r'[^a-zA-Z0-9-_~,.$\(\)# @]').search

def checkValidId(self, id, allow_dup=0):
# If allow_dup is false, an error will be raised if an object
Expand Down Expand Up @@ -569,6 +577,92 @@ def tpValues(self):
r.append(o)
return r

security.declareProtected(import_export_objects, 'manage_exportObject')
def manage_exportObject(self, id='', download=None, toxml=None,
RESPONSE=None,REQUEST=None):
"""Exports an object to a file and returns that file."""
if not id:
# can't use getId() here (breaks on "old" exported objects)
id=self.id
if hasattr(id, 'im_func'): id=id()
ob=self
else: ob=self._getOb(id)

suffix=toxml and 'xml' or 'zexp'

if download:
f=StringIO()
if toxml:
exportXML(ob._p_jar, ob._p_oid, f)
else:
ob._p_jar.exportFile(ob._p_oid, f)
if RESPONSE is not None:
RESPONSE.setHeader('Content-type','application/data')
RESPONSE.setHeader('Content-Disposition',
'inline;filename=%s.%s' % (id, suffix))
return f.getvalue()

cfg = getConfiguration()
f = os.path.join(cfg.clienthome, '%s.%s' % (id, suffix))
if toxml:
exportXML(ob._p_jar, ob._p_oid, f)
else:
ob._p_jar.exportFile(ob._p_oid, f)

if REQUEST is not None:
return self.manage_main(self, REQUEST,
manage_tabs_message=
'<em>%s</em> successfully exported to <em>%s</em>' % (id,f),
title = 'Object exported')


security.declareProtected(import_export_objects, 'manage_importExportForm')
manage_importExportForm=DTMLFile('dtml/importExport',globals())

security.declareProtected(import_export_objects, 'manage_importObject')
def manage_importObject(self, file, REQUEST=None, set_owner=1):
"""Import an object from a file"""
dirname, file=os.path.split(file)
if dirname:
raise BadRequest, 'Invalid file name %s' % escape(file)

for impath in self._getImportPaths():
filepath = os.path.join(impath, 'import', file)
if os.path.exists(filepath):
break
else:
raise BadRequest, 'File does not exist: %s' % escape(file)

self._importObjectFromFile(filepath, verify=not not REQUEST,
set_owner=set_owner)

if REQUEST is not None:
return self.manage_main(
self, REQUEST,
manage_tabs_message='<em>%s</em> successfully imported' % id,
title='Object imported',
update_menu=1)

def _importObjectFromFile(self, filepath, verify=1, set_owner=1):
# locate a valid connection
connection=self._p_jar
obj=self

while connection is None:
obj=obj.aq_parent
connection=obj._p_jar
ob=connection.importFile(
filepath, customImporters=customImporters)
if verify: self._verifyObjectPaste(ob, validate_src=0)
id=ob.id
if hasattr(id, 'im_func'): id=id()
self._setObject(id, ob, set_owner=set_owner)

# try to make ownership implicit if possible in the context
# that the object was imported into.
ob=self._getOb(id)
ob.manage_changeOwnershipType(explicit=0)

def _getImportPaths(self):
cfg = getConfiguration()
paths = []
Expand Down
124 changes: 124 additions & 0 deletions src/OFS/XMLExportImport.py
@@ -0,0 +1,124 @@
##############################################################################
#
# Copyright (c) 2002 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 base64 import encodestring
from cStringIO import StringIO
from ZODB.serialize import referencesf
from ZODB.ExportImport import TemporaryFile, export_end_marker
from ZODB.utils import p64
from ZODB.utils import u64
from Shared.DC.xml import ppml


magic='<?xm' # importXML(jar, file, clue)}

def XMLrecord(oid, len, p):
q=ppml.ToXMLUnpickler
f=StringIO(p)
u=q(f)
id=u64(oid)
aka=encodestring(oid)[:-1]
u.idprefix=str(id)+'.'
p=u.load().__str__(4)
if f.tell() < len:
p=p+u.load().__str__(4)
String=' <record id="%s" aka="%s">\n%s </record>\n' % (id, aka, p)
return String

def exportXML(jar, oid, file=None):

if file is None: file=TemporaryFile()
elif type(file) is str: file=open(file,'w+b')
write=file.write
write('<?xml version="1.0"?>\012<ZopeData>\012')
ref=referencesf
oids=[oid]
done_oids={}
done=done_oids.has_key
load=jar._storage.load
while oids:
oid=oids[0]
del oids[0]
if done(oid): continue
done_oids[oid]=1
try:
try:
p, serial = load(oid)
except TypeError:
# Some places inside the ZODB 3.9 still want a version
# argument, for example TmpStore from Connection.py
p, serial = load(oid, None)
except:
pass # Ick, a broken reference
else:
ref(p, oids)
write(XMLrecord(oid,len(p),p))
write('</ZopeData>\n')
return file

class zopedata:
def __init__(self, parser, tag, attrs):
self.file=parser.file
write=self.file.write
write('ZEXP')

def append(self, data):
file=self.file
write=file.write
pos=file.tell()
file.seek(pos)
write(data)

def start_zopedata(parser, tag, data):
return zopedata(parser, tag, data)

def save_zopedata(parser, tag, data):
file=parser.file
write=file.write
pos=file.tell()
file.seek(pos)
write(export_end_marker)

def save_record(parser, tag, data):
file=parser.file
write=file.write
pos=file.tell()
file.seek(pos)
a=data[1]
if a.has_key('id'): oid=a['id']
oid=p64(int(oid))
v=''
for x in data[2:]:
v=v+x
l=p64(len(v))
v=oid+l+v
return v

def importXML(jar, file, clue=''):
import xml.parsers.expat
if type(file) is str:
file=open(file, 'rb')
outfile=TemporaryFile()
data=file.read()
F=ppml.xmlPickler()
F.end_handlers['record'] = save_record
F.end_handlers['ZopeData'] = save_zopedata
F.start_handlers['ZopeData'] = start_zopedata
F.binary=1
F.file=outfile
p=xml.parsers.expat.ParserCreate()
p.CharacterDataHandler=F.handle_data
p.StartElementHandler=F.unknown_starttag
p.EndElementHandler=F.unknown_endtag
r=p.Parse(data)
outfile.seek(0)
return jar.importFile(outfile,clue)

0 comments on commit c40714a

Please sign in to comment.