work in progress: confiburable storage for resources; + some minor tweaks

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1405 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-10-16 09:27:44 +00:00
parent 83947823df
commit 3c158f2f93
10 changed files with 150 additions and 35 deletions

View file

@ -549,10 +549,10 @@ view for rendering.)
>>> component.provideAdapter(LoopsType)
>>> view = NodeView(m112, TestRequest())
>>> view.renderTarget()
u''
u'<pre></pre>'
>>> doc1.data = u'Test data\n\nAnother paragraph'
>>> view.renderTarget()
u'Test data\n\nAnother paragraph'
u'<pre>Test data\n\nAnother paragraph</pre>'
>>> doc1.contentType = 'text/restructured'
>>> view.renderTarget()
u'<p>Test data</p>\n<p>Another paragraph</p>\n'
@ -667,6 +667,18 @@ created object:
>>> sorted(t.__name__ for t in note.getConcepts())
[u'note', u'topic']
When creating an object its name is automatically generated using the title
of the object. Let's make sure that the name chooser also handles special
and possibly critcal cases:
>>> nc = ResourceNameChooser(resources)
>>> nc.chooseName(u'abc: (cde)', None)
u'abc_cde'
>>> nc.chooseName(u'\xdcml\xe4ut', None)
u'uemlaeut'
>>> nc.chooseName(u'A very very loooooong title', None)
u'a_title'
Editing an object
-----------------

View file

@ -235,12 +235,36 @@ class CreateObject(EditObject):
return True
specialCharacters = {
'\xc4': 'Ae', '\xe4': 'ae', '\xd6': 'Oe', '\xf6': 'oe',
'\xdc': 'Ue', '\xfc': 'ue', '\xdf': 'ss'}
class ResourceNameChooser(NameChooser):
adapts(IResourceManager)
def chooseName(self, title, obj):
name = title.replace(' ', '_').lower()
name = super(ResourceNameChooser, self).chooseName(name, obj)
return name
result = []
if len(title) > 15:
words = title.split()
if len(words) > 1:
title = '_'.join((words[0], words[-1]))
for c in title:
try:
c = c.encode('ISO8859-15')
except UnicodeEncodeError:
continue
if c in specialCharacters:
result.append(specialCharacters[c].lower())
continue
if ord(c) > 127:
c = chr(ord(c) & 127)
if c in ('_., '):
result.append('_')
elif not c.isalpha() and not c.isdigit():
continue
else:
result.append(c.lower())
name = unicode(''.join(result))
return super(ResourceNameChooser, self).chooseName(name, obj)

View file

@ -288,6 +288,8 @@ class NodeView(BaseView):
targetId = self.targetId
if targetId is not None:
return '%s/.target%s' % (self.url, targetId)
else:
return self.url
@Lazy
def realTargetUrl(self):

View file

@ -34,6 +34,7 @@ from zope.formlib.interfaces import DISPLAY_UNWRITEABLE
from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy
from zope.documenttemplate.dt_util import html_quote
from cybertools.typology.interfaces import IType
from loops.interfaces import IBaseResource, IDocument, IMediaAsset
@ -112,10 +113,14 @@ class ResourceView(BaseView):
return self
def show(self):
data = self.context.data
context = self.context
data = context.data
response = self.request.response
response.setHeader('Content-Type', self.context.contentType)
response.setHeader('Content-Type', context.contentType)
response.setHeader('Content-Length', len(data))
if not context.contentType.startswith('image/'):
response.setHeader('Content-Disposition',
'attachment; filename=%s' % zapi.getName(context))
return data
def concepts(self):
@ -202,9 +207,12 @@ class DocumentView(ResourceView):
""" Return the rendered content (data) of the context object.
"""
text = self.context.data
typeKey = renderingFactories.get(self.context.contentType, None)
contentType = self.context.contentType
typeKey = renderingFactories.get(contentType, None)
if typeKey is None:
return text
if contentType == 'text/html':
return text
return u'<pre>%s</pre>' % html_quote(text)
source = zapi.createObject(typeKey, text)
view = zapi.getMultiAdapter((removeAllProxies(source), self.request))
return view.render()

View file

@ -340,6 +340,11 @@
<adapter factory="loops.target.ConceptProxy"
permission="zope.ManageContent" />
<adapter for="loops.interfaces.IFile"
provides="cybertools.text.interfaces.ITextTransform"
name="application/pdf"
factory="cybertools.text.pdf.PdfTransform" />
<vocabulary
factory="loops.concept.ConceptTypeSourceList"
name="loops.conceptTypeSource"

View file

@ -206,6 +206,23 @@ Now let's have a look at resources.
>>> img1_type.title
u'File'
Using the type machinery we can also specify options that may be used
for controlling e.g. storage for external files.
>>> extfile = concepts['extfile'] = Concept(u'External File')
>>> ef1 = resources['ef1'] = Resource(u'Extfile #1')
>>> ef1.resourceType = extfile
>>> ef1_type = IType(ef1)
>>> IType(ef1).options
[]
>>> extfile_ad = TypeConcept(extfile)
>>> extfile_ad.options = ['dummy', 'storage:varsubdir',
... 'storage_parameters:extfiles']
>>> IType(ef1).options
['dummy', 'storage:varsubdir', 'storage_parameters:extfiles']
>>> IType(ef1).optionsDict
{'default': ['dummy'], 'storage_parameters': 'extfiles', 'storage': 'varsubdir'}
Can we find out somehow which types are available? This is the time to look
for a type manager. This could be a utility; but in the loops package it
is again an adapter, now for the loops root object. Nevertheless one can

View file

@ -539,6 +539,13 @@ class ITypeConcept(Interface):
default=u'',
required=False)
options = schema.List(
title=_(u'Options'),
description=_(u'Additional settings.'),
value_type=schema.TextLine(),
default=[],
required=False)
# storage = schema.Choice()
@ -555,6 +562,12 @@ class IFile(IResourceAdapter, IResourceSchema):
"""
class IExternalFile(IFile):
""" A file whose content (data attribute) is not stored in the ZODB
but somewhere else, typically in the file system.
"""
class IImage(IResourceAdapter):
""" A media asset that may be embedded in a (web) page as an image.
"""
@ -590,26 +603,3 @@ class IViewConfiguratorSchema(Interface):
required=False)
# the next two interfaces are obsolete, they will be replaced by IResourceStorage:
class IFileSystemResource(Interface):
fsPath = schema.BytesLine(
title=_(u'Filesystem Path'),
description=_(u'Optional path to a file in the filesystem '
'to be used for storing the resource'),
default='',
missing_value='',
required=False)
class IControlledResource(Interface):
readOnly = schema.Bool(
title=_(u'Read only'),
description=_(u'Check this if resource may not be modified '
'after being first filled with non-empty content'),
default=False,
required=False)

View file

@ -22,6 +22,7 @@ Definition of the Concept class.
$Id$
"""
from zope import component
from zope.app import zapi
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
@ -42,6 +43,7 @@ from zope.event import notify
from cybertools.relation.registry import getRelations
from cybertools.relation.interfaces import IRelatable
from cybertools.storage.interfaces import IExternalStorage
from cybertools.text.interfaces import ITextTransform
from cybertools.typology.interfaces import IType, ITypeManager
@ -208,6 +210,38 @@ class FileAdapter(ResourceAdapterBase):
data = property(getData, setData)
class ExternalFileAdapter(FileAdapter):
@Lazy
def externalAddress(self):
# or is this an editable attribute?
# or some sort of subpath set during import?
# anyway: an attribute of the context object.
return self.context.__name__
@Lazy
def options(self):
return IType(self.context).optionsDict
@Lazy
def storageName(self):
return self.options.get('storage')
@Lazy
def storageParams(self):
return self.options.get('storage_parameters')
def setData(self, data):
storage = component.getUtility(IExternalStorage, name=self.storageName)
storage.setData(self.externalAddress, data, params=self.storageParams)
def getData(self):
storage = component.getUtility(IExternalStorage)
return storage.getData(self.externalAddress, params=self.storageParams)
data = property(getData, setData)
class DocumentAdapter(ResourceAdapterBase):
""" Common base class for all resource types with a text-like
data attribute.
@ -280,8 +314,8 @@ class IndexAttributes(object):
ti = IType(context).typeInterface
if ti is not None:
adapted = ti(context)
transform = component.queryAdapter(
adapted, ITextTransform, name=context.contentType)
transform = component.queryAdapter(adapted, ITextTransform,
name=context.contentType)
if transform is not None:
rfa = component.queryAdapter(IReadFile, adapted)
if rfa is None:

23
type.py
View file

@ -99,6 +99,21 @@ class LoopsType(BaseType):
# TODO: unify this type attribute naming...
return self.context.resourceType
@Lazy
def options(self):
return ITypeConcept(self.typeProvider).options or []
@Lazy
def optionsDict(self):
result = {'default': []}
for opt in self.options:
if ':' in opt:
key, value = opt.split(':', 1)
result[key] = value
else:
result['default'].append(opt)
return result
class LoopsTypeInfo(LoopsType):
""" The type info class used by the type manager for listing types.
@ -237,6 +252,14 @@ class TypeConcept(AdapterBase):
self.context._typeInterface = ifc
typeInterface = property(getTypeInterface, setTypeInterface)
def getOptions(self):
return getattr(self.context, '_options', [])
#return super(TypeConcept, self).options or []
def setOptions(self, value):
self.context._options = value
#super(TypeConcept, self).options = value
options = property(getOptions, setOptions)
class TypeInterfaceSourceList(object):

View file

@ -196,4 +196,4 @@ class NodeTraverser(ItemTraverser):
request.annotations['loops.view'] = viewAnnotations
return self.context
return super(NodeTraverser, self).publishTraverse(request, name)