work in progress: sub-elements, e.g. for annotations and state information, for export/import

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2477 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-03-28 12:42:47 +00:00
parent d944aca0f5
commit 9dec6887b6
10 changed files with 221 additions and 36 deletions

View file

@ -272,9 +272,9 @@
provides="loops.interfaces.IExternalFile" />
<class class="loops.resource.ExternalFileAdapter">
<require permission="zope.View"
interface="loops.interfaces.IFile" />
interface="loops.interfaces.IExternalFile" />
<require permission="zope.ManageContent"
set_schema="loops.interfaces.IFile" />
set_schema="loops.interfaces.IExternalFile" />
</class>
<adapter factory="loops.resource.TextDocumentAdapter" trusted="True"
@ -409,6 +409,7 @@
<include package=".rest" />
<include package=".search" />
<include package=".security" />
<include package=".stateful" />
<include package=".versioning" />
<include package=".xmlrpc" />

27
external/README.txt vendored
View file

@ -74,6 +74,19 @@ Working with nodes
>>> elements = reader.read(input)
>>> loader.load(elements)
Sub-elements
------------
>>> from loops.external import annotation
>>> input = """concept('myquery', u'My Query', 'query', viewName='mystuff.html')[
... annotations(creators='john')]"""
>>> elements = reader.read(input)
>>> elements[0].subElements
[{'creators': 'john'}]
>>> loader.load(elements)
[('creators', 'john')]
Exporting loops Objects
=======================
@ -107,6 +120,20 @@ Writing object information to the external storage
node('home', u'Home', '', u'menu', body=u'Welcome')
node('myquery', u'My Query', 'home', u'page', target=u'concepts/myquery')...
Writing subElements
-------------------
>>> input = """concept('myquery', u'My Query', 'query', viewName='mystuff.html')[
... annotations(creators='john'),
... annotations(modified='2007-08-12')]"""
>>> elements = reader.read(input)
>>> output = StringIO()
>>> writer.write(elements, output)
>>> print output.getvalue()
concept('myquery', u'My Query', 'query', viewName='mystuff.html')[
annotations(creators='john'),
annotations(modified='2007-08-12')]...
The Export/Import View
======================

57
external/annotation.py vendored Normal file
View file

@ -0,0 +1,57 @@
#
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Export/import of annotations.
$Id$
"""
from zope.component import adapts
from zope.interface import implements
from loops.external.element import Element, elementTypes
from loops.external.interfaces import ISubExtractor
from loops.interfaces import ILoopsObject
class AnnotationsElement(Element):
elementType = 'annotations'
def __init__(self, **kw):
for k, v in kw.items():
self[k] = v
def __call__(self, loader):
print self.items()
class AnnotationsExtractor(object):
implements(ISubExtractor)
adapts(ILoopsObject)
def extract(self):
return []
elementTypes.update(dict(
annotations=AnnotationsElement,
))

63
external/base.py vendored
View file

@ -36,7 +36,7 @@ from cybertools.composer.interfaces import IInstance
from cybertools.composer.schema.interfaces import ISchemaFactory
from cybertools.typology.interfaces import IType
from loops.common import adapted
from loops.external.interfaces import ILoader, IExtractor
from loops.external.interfaces import ILoader, IExtractor, ISubExtractor
from loops.external.element import elementTypes
from loops.interfaces import IConceptSchema, IResourceSchema
from loops.resource import Document, MediaAsset
@ -81,6 +81,8 @@ class Loader(Base, SetupManager):
def load(self, elements):
for element in elements:
element(self)
if element.subElements is not None:
self.load(element.subElements)
# TODO: care for setting attributes via Instance (Editor)
# instead of using SetupManager methods:
@ -103,7 +105,9 @@ class Extractor(Base):
typeElement = elementTypes['type']
for obj in self.typeConcept.getChildren([self.typePredicate]):
data = self.getObjectData(obj)
yield typeElement(getName(obj), obj.title, **data)
element = typeElement(getName(obj), obj.title, **data)
self.provideSubElements(obj, element)
yield element
def extractConcepts(self):
conceptElement = elementTypes['concept']
@ -112,7 +116,9 @@ class Extractor(Base):
if obj.conceptType != typeConcept:
data = self.getObjectData(obj)
tp = getName(obj.conceptType)
yield conceptElement(name, obj.title, tp, **data)
element = conceptElement(name, obj.title, tp, **data)
self.provideSubElements(obj, element)
yield element
def extractResources(self):
elementClass = elementTypes['resource']
@ -123,26 +129,9 @@ class Extractor(Base):
tp = 'textdocument'
element = elementClass(name, obj.title, tp, **data)
element.processExport(self)
self.provideSubElements(obj, element)
yield element
def getObjectData(self, obj, defaultInterface=IConceptSchema):
aObj = adapted(obj)
schemaFactory = component.getAdapter(aObj, ISchemaFactory)
ti = IType(obj).typeInterface or defaultInterface
schema = schemaFactory(ti, manager=self) #, request=self.request)
instance = IInstance(aObj)
instance.template = schema
# TODO: use ``_not_exportable`` attribute of adapter to control export;
# this should also convert object attributes like e.g. typeInterface
#data = instance.applyTemplate(mode='export')
data = instance.applyTemplate(mode='edit')
if 'title' in data:
del data['title']
data['description'] = obj.description
if not data['description']:
del data['description']
return data
def extractChildren(self):
childElement = elementTypes['child']
typePredicate = self.typePredicate
@ -172,7 +161,7 @@ class Extractor(Base):
def extractNodes(self, parent=None, path=''):
if parent is None:
parent = self.views
element = elementTypes['node']
elementClass = elementTypes['node']
for name, obj in parent.items():
data = {}
for attr in ('description', 'body', 'viewName'):
@ -182,8 +171,36 @@ class Extractor(Base):
target = obj.target
if target is not None:
data['target'] = '/'.join((getName(getParent(target)), getName(target)))
yield element(name, obj.title, path, obj.nodeType, **data)
elem = elementClass(name, obj.title, path, obj.nodeType, **data)
self.provideSubElements(obj, elem)
yield elem
childPath = path and '/'.join((path, name)) or name
for elem in self.extractNodes(obj, childPath):
self.provideSubElements(obj, elem)
yield elem
# helper methods
def getObjectData(self, obj, defaultInterface=IConceptSchema):
aObj = adapted(obj)
schemaFactory = component.getAdapter(aObj, ISchemaFactory)
ti = IType(obj).typeInterface or defaultInterface
schema = schemaFactory(ti, manager=self) #, request=self.request)
instance = IInstance(aObj)
instance.template = schema
# TODO: use ``_not_exportable`` attribute of adapter to control export;
# this should also convert object attributes like e.g. typeInterface
#data = instance.applyTemplate(mode='export')
data = instance.applyTemplate(mode='edit')
if 'title' in data:
del data['title']
data['description'] = obj.description
if not data['description']:
del data['description']
return data
def provideSubElements(self, obj, element):
for name, extractor in component.getAdapters((obj,), ISubExtractor):
for sub in extractor.extract():
element.add(sub)

2
external/browser.py vendored
View file

@ -89,7 +89,7 @@ class ExportImport(object):
return False
reader = component.getUtility(IReader)
elements = reader.read(data)
loader = Loader(self.context, )
loader = Loader(self.context)
loader.load(elements)
self.message = u'Content uploaded and imported.'
return False

29
external/element.py vendored
View file

@ -37,6 +37,10 @@ class Element(dict):
implements(IElement)
elementType = ''
posArgs = ()
object = None
parent = None
subElements = None
def __init__(self, name, title, type=None, *args, **kw):
self['name'] = name
@ -46,9 +50,24 @@ class Element(dict):
for k, v in kw.items():
self[k] = v
def __getitem__(self, key):
if isinstance(key, Element):
key = (key,)
if isinstance(key, tuple):
for item in key:
item.parent = self
self.add(item)
return key
return super(Element, self).__getitem__(key)
def processExport(self, extractor):
pass
def add(self, element):
if self.subElements is None:
self.subElements = []
self.subElements.append(element)
def __call__(self, loader):
pass
@ -62,7 +81,7 @@ class ConceptElement(Element):
type = loader.concepts[self['type']]
kw = dict((k, v) for k, v in self.items()
if k not in self.posArgs)
loader.addConcept(self['name'], self['title'], type, **kw)
self.object = loader.addConcept(self['name'], self['title'], type, **kw)
class TypeElement(ConceptElement):
@ -83,7 +102,8 @@ class TypeElement(ConceptElement):
ti = self.get('typeInterface')
if ti:
kw['typeInterface'] = resolve(ti)
loader.addConcept(self['name'], self['title'], loader.typeConcept, **kw)
self.object = loader.addConcept(self['name'], self['title'],
loader.typeConcept, **kw)
class ChildElement(Element):
@ -128,7 +148,7 @@ class ResourceElement(Element):
content = content.decode('UTF-8')
kw['data'] = content
f.close()
loader.addResource(self['name'], self['title'], type, **kw)
self.object = loader.addResource(self['name'], self['title'], type, **kw)
class ResourceRelationElement(ChildElement):
@ -160,6 +180,7 @@ class NodeElement(Element):
if target is not None:
targetObject = traverse(loader.context, target, None)
node.target = targetObject
self.object = node
# element registry
@ -172,3 +193,5 @@ elementTypes = dict(
resourceRelation=ResourceRelationElement,
node=NodeElement,
)
toplevelElements = ('type', 'concept', 'resource', 'resourceRelation', 'node')

View file

@ -36,6 +36,21 @@ class IElement(Interface):
or IElement objects.
"""
elementType = Attribute('A string denoting the element type.')
object = Attribute('The object that has been created from this '
'element during import.')
parent = Attribute('An optional parent element that this element is part of.')
subElements = Attribute('An optional list of sub-elements; initially None.')
def processExport(extractor):
""" Will be called by the extractor during export to allow for
special handling e.g. of certain attributes.
"""
def add(element):
""" Add a sub-element, may be called by the extractor during export.
"""
def __call__(loader):
""" Create the object that is specified by the element in the
context of the loader and return it.
@ -75,7 +90,8 @@ class IWriter(Interface):
class IExtractor(Interface):
""" Extracts information from loops objects and provides them as
IElement objects. Will typically be used as an adapter.
IElement objects. Will typically be used as an adapter on the
loops root object.
"""
def extract():
@ -83,3 +99,7 @@ class IExtractor(Interface):
the content of the context object.
"""
class ISubExtractor(IExtractor):
""" Used for extracting special informations from individual objects
that will be represented by sub-elements.
"""

22
external/pyfunc.py vendored
View file

@ -27,7 +27,7 @@ from zope.cachedescriptors.property import Lazy
from zope.interface import implements
from loops.external.interfaces import IReader, IWriter
from loops.external.element import elementTypes
from loops.external.element import elementTypes, toplevelElements
class PyReader(object):
@ -50,7 +50,8 @@ class InputProcessor(dict):
def __getitem__(self, key):
def factory(*args, **kw):
element = elementTypes[key](*args, **kw)
self.elements.append(element)
if key in toplevelElements:
self.elements.append(element)
return element
return factory
@ -59,8 +60,8 @@ class PyWriter(object):
implements(IWriter)
def write(self, elements, output):
for element in elements:
def write(self, elements, output, level=0):
for idx, element in enumerate(elements):
args = []
for arg in element.posArgs:
if arg in element:
@ -68,7 +69,18 @@ class PyWriter(object):
for k, v in element.items():
if k not in element.posArgs:
args.append("%s=%s" % (str(k), repr(v)))
output.write('%s(%s)\n' % (element.elementType, ', '.join(args)))
if not element.subElements:
output.write('%s%s(%s)'
% (level*' ', element.elementType, ', '.join(args)))
else:
output.write('%s%s(%s)[\n'
% (level*' ', element.elementType, ', '.join(args)))
self.write(element.subElements, output, level+1)
output.write(']')
if level == 0:
output.write('\n')
elif idx < len(elements) - 1:
output.write(',\n')
def toStr(value):

View file

@ -662,7 +662,35 @@ class IFile(IResourceAdapter, IResourceSchema):
localFilename = Attribute('Filename provided during upload.')
class IStorageInfo(Interface):
storageName = schema.BytesLine(
title=_(u'Storage Name'),
description=_(u'The name of a storage utility used for this '
'object.'),
default='',
missing_value='',
required=False)
storageParams = schema.BytesLine(
title=_(u'Storage Parameters'),
description=_(u'Information used to address the external '
'storage, e.g. a filename or path.'),
default='',
missing_value='',
required=False)
externalAddress = schema.BytesLine(
title=_(u'External Address'),
description=_(u'The full address for accessing the object '
'on the external storage, e.g. a filename or path.'),
default='',
missing_value='',
required=False)
class IExternalFile(IFile):
#class IExternalFile(IFile, IStorageInfo):
""" A file whose content (data attribute) is not stored in the ZODB
but somewhere else, typically in the file system.
"""

View file

@ -6,7 +6,7 @@
i18n_domain="loops">
<zope:utility
factory="cybertools.stateful.publishing.simplePublishing"
factory="cybertools.stateful.publishing.simplePublishingFactory"
name="loops.simple_publishing" />
<zope:adapter