storage improvements; resource listings + 'author(s)' field; start with user_registration.html

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1671 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-04-01 07:34:00 +00:00
parent 878c2f58c8
commit 1e115ed0e2
19 changed files with 169 additions and 61 deletions

View file

@ -651,7 +651,7 @@ on data provided in this form:
>>> from loops.type import TypeConcept >>> from loops.type import TypeConcept
>>> from loops.resource import NoteAdapter >>> from loops.resource import NoteAdapter
>>> component.provideAdapter(TypeConcept) >>> component.provideAdapter(TypeConcept)
>>> component.provideAdapter(NoteAdapter) >>> component.provideAdapter(NoteAdapter, provides=INote)
>>> note_tc = concepts['note'] = Concept('Note') >>> note_tc = concepts['note'] = Concept('Note')
>>> note_tc.conceptType = typeObject >>> note_tc.conceptType = typeObject
>>> ITypeConcept(note_tc).typeInterface = INote >>> ITypeConcept(note_tc).typeInterface = INote

View file

@ -25,6 +25,7 @@ $Id$
from zope.app import zapi from zope.app import zapi
from zope import component from zope import component
from zope.app.form.browser.interfaces import ITerms from zope.app.form.browser.interfaces import ITerms
from zope.app.security.interfaces import IAuthentication
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve from zope.dottedname.resolve import resolve
from zope.dublincore.interfaces import IZopeDublinCore from zope.dublincore.interfaces import IZopeDublinCore
@ -118,6 +119,17 @@ class BaseView(GenericView):
d = dc.modified or dc.created d = dc.modified or dc.created
return d and d.strftime('%Y-%m-%d %H:%M') or '' return d and d.strftime('%Y-%m-%d %H:%M') or ''
@Lazy
def creators(self):
cr = IZopeDublinCore(self.context).creators or []
pau = component.getUtility(IAuthentication)
creators = []
for c in cr:
principal = pau.getPrincipal(c)
if principal is not None:
creators.append(principal.title)
return ', '.join(creators)
@Lazy @Lazy
def loopsRoot(self): def loopsRoot(self):
return self.context.getLoopsRoot() return self.context.getLoopsRoot()

View file

@ -86,6 +86,7 @@
tal:condition="view/useVersioning">V</th> tal:condition="view/useVersioning">V</th>
<th i18n:translate="label_size">Size</th> <th i18n:translate="label_size">Size</th>
<th i18n:translate="label_modifdate">Modification Date</th> <th i18n:translate="label_modifdate">Modification Date</th>
<th i18n:translate="label_authors">Author(s)</th>
</tr> </tr>
<tal:items repeat="related resources"> <tal:items repeat="related resources">
<tal:item define="class python: repeat['related'].odd() and 'even' or 'odd'; <tal:item define="class python: repeat['related'].odd() and 'even' or 'odd';
@ -111,13 +112,8 @@
<td style="text-align: right"> <td style="text-align: right">
<span tal:replace="related/context/sizeForDisplay">Type</span> <span tal:replace="related/context/sizeForDisplay">Type</span>
</td> </td>
<td><span tal:replace="related/modified">Type</span></td> <td><span tal:replace="related/modified">2007-03-30</span></td>
</tr> <td><span tal:replace="related/creators">John</span></td>
<tr tal:condition="nothing"
tal:attributes="class class">
<td colspan="4" style="padding-left: 7em">
<i tal:content="description">describing...</i>
</td>
</tr> </tr>
</tal:item> </tal:item>
</tal:items> </tal:items>

View file

@ -77,10 +77,10 @@
for="zope.interface.Interface" for="zope.interface.Interface"
name="loops.pageform" /> name="loops.pageform" />
<zope:adapter <!--<zope:adapter
factory="loops.browser.util.dataform" factory="loops.browser.util.dataform"
for="zope.interface.Interface" for="zope.interface.Interface"
name="loops.dataform" /> name="loops.dataform" />-->
<zope:adapter <zope:adapter
factory="loops.browser.util.concept_macros" factory="loops.browser.util.concept_macros"

View file

@ -23,7 +23,7 @@
<div metal:fill-slot="body"> <div metal:fill-slot="body">
<div metal:define-macro="form"> <div metal:define-macro="content">
<form action="." metal:define-macro="master" <form action="." metal:define-macro="master"
tal:attributes="action request/URL" method="post" tal:attributes="action request/URL" method="post"

View file

@ -282,7 +282,7 @@ class EditObject(FormController):
self.collectConcepts(fn[len(self.conceptPrefix):], value) self.collectConcepts(fn[len(self.conceptPrefix):], value)
else: else:
if not value and fn == 'data' and IFile.providedBy(adapted): if not value and fn == 'data' and IFile.providedBy(adapted):
# empty file data - don' change # empty file data - don't change
continue continue
if isinstance(value, FileUpload): if isinstance(value, FileUpload):
filename = getattr(value, 'filename', '') filename = getattr(value, 'filename', '')
@ -345,6 +345,9 @@ class CreateObject(EditObject):
data = form.get('form.data') data = form.get('form.data')
if data and isinstance(data, FileUpload): if data and isinstance(data, FileUpload):
name = getattr(data, 'filename', None) name = getattr(data, 'filename', None)
# strip path from IE uploads:
if '\\' in name:
name = name.rsplit('\\', 1)[-1]
else: else:
name = None name = None
name = INameChooser(container).chooseName(name, obj) name = INameChooser(container).chooseName(name, obj)

View file

@ -62,7 +62,8 @@ class NodeView(BaseView):
_itemNum = 0 _itemNum = 0
template = NamedTemplate('loops.node_macros') #template = NamedTemplate('loops.node_macros')
template = node_macros
@Lazy @Lazy
def macro(self): def macro(self):
@ -74,7 +75,7 @@ class NodeView(BaseView):
media='all', position=3) media='all', position=3)
cm.register('js', 'loops.js', resourceName='loops.js') cm.register('js', 'loops.js', resourceName='loops.js')
cm.register('portlet_left', 'navigation', title='Navigation', cm.register('portlet_left', 'navigation', title='Navigation',
subMacro=self.template.macros['menu']) subMacro=node_macros.macros['menu'])
#if not IUnauthenticatedPrincipal.providedBy(self.request.principal): #if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
if canWrite(self.context, 'title'): if canWrite(self.context, 'title'):
#cm.register('portlet_right', 'clipboard', title='Clipboard', #cm.register('portlet_right', 'clipboard', title='Clipboard',
@ -84,7 +85,7 @@ class NodeView(BaseView):
# see controller / configurator: use multiple configurators; # see controller / configurator: use multiple configurators;
# register additional configurators (adapters) from within package. # register additional configurators (adapters) from within package.
cm.register('portlet_right', 'actions', title='Actions', cm.register('portlet_right', 'actions', title='Actions',
subMacro=self.template.macros['actions']) subMacro=node_macros.macros['actions'])
@Lazy @Lazy
def view(self): def view(self):

View file

@ -123,7 +123,8 @@
</div><br /> </div><br />
<div tal:repeat="item item/pageItems"> <div tal:repeat="item item/pageItems">
<a href="#" <a href="#"
tal:attributes="href item/url" tal:attributes="href item/url;
title item/description"
tal:content="item/title">Item</a> tal:content="item/title">Item</a>
</div> </div>
</metal:body> </metal:body>
@ -142,13 +143,9 @@
tal:condition="nocall:target"> tal:condition="nocall:target">
<div tal:repeat="related item/resources"> <div tal:repeat="related item/resources">
<a href="#" <a href="#"
tal:attributes="href string:${view/url}/.target${related/uniqueId}" tal:attributes="href string:${view/url}/.target${related/uniqueId};
title related/description"
tal:content="related/title">Resource Title</a> tal:content="related/title">Resource Title</a>
<div style="margin-left: 5em"
tal:define="description related/description"
tal:condition="description">
<i tal:content="description">Description</i>
</div>
</div> </div>
</div> </div>
</metal:resources> </metal:resources>
@ -169,13 +166,9 @@
tal:condition="nocall:target"> tal:condition="nocall:target">
<div tal:repeat="related item/children"> <div tal:repeat="related item/children">
<a href="#" <a href="#"
tal:attributes="href string:${view/url}/.target${related/uniqueId}" tal:attributes="href string:${view/url}/.target${related/uniqueId};
title related/description"
tal:content="related/title">Resource Title</a> tal:content="related/title">Resource Title</a>
<div style="margin-left: 5em"
tal:define="description related/description"
tal:condition="description">
<i tal:content="description">Description</i>
</div>
</div> </div>
</div> </div>
</metal:children> </metal:children>

View file

@ -27,11 +27,13 @@ from zope import component
from zope.app import zapi from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog from zope.app.catalog.interfaces import ICatalog
from zope.dublincore.interfaces import ICMFDublinCore from zope.dublincore.interfaces import ICMFDublinCore
from zope.app.form.browser.textwidgets import FileWidget
from zope.app.pagetemplate import ViewPageTemplateFile from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.formlib.form import FormFields from zope.formlib.form import FormFields
from zope.formlib.interfaces import DISPLAY_UNWRITEABLE from zope.formlib.interfaces import DISPLAY_UNWRITEABLE
from zope.proxy import removeAllProxies from zope.proxy import removeAllProxies
from zope.schema.interfaces import IBytes
from zope.security import canAccess, canWrite from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
@ -54,6 +56,14 @@ renderingFactories = {
} }
class CustomFileWidget(FileWidget):
def hasInput(self):
print 'hasInput', self.request.form.get(self.name)
if not self.request.form.get(self.name):
return False
class ResourceEditForm(EditForm): class ResourceEditForm(EditForm):
@Lazy @Lazy
@ -67,6 +77,9 @@ class ResourceEditForm(EditForm):
if typeInterface is not None: if typeInterface is not None:
omit = [f for f in typeInterface if f in IBaseResource] omit = [f for f in typeInterface if f in IBaseResource]
fields = FormFields(fields.omit(*omit), typeInterface) fields = FormFields(fields.omit(*omit), typeInterface)
dataField = fields['data']
if IBytes.providedBy(dataField):
dataField.customWidget = CustomFileWidget
return fields return fields
def setUpWidgets(self, ignore_request=False): def setUpWidgets(self, ignore_request=False):

View file

@ -30,7 +30,8 @@ from zope.formlib.namedtemplate import NamedTemplateImplementation
pageform = NamedTemplateImplementation(ViewPageTemplateFile('pageform.pt')) pageform = NamedTemplateImplementation(ViewPageTemplateFile('pageform.pt'))
dataform = NamedTemplateImplementation(ViewPageTemplateFile('dataform.pt')) #dataform = NamedTemplateImplementation(ViewPageTemplateFile('dataform.pt'))
dataform = ViewPageTemplateFile('dataform.pt')
concept_macros = NamedTemplateImplementation(ViewPageTemplateFile('concept_macros.pt')) concept_macros = NamedTemplateImplementation(ViewPageTemplateFile('concept_macros.pt'))
node_macros = NamedTemplateImplementation(ViewPageTemplateFile('node_macros.pt')) node_macros = NamedTemplateImplementation(ViewPageTemplateFile('node_macros.pt'))

View file

@ -29,6 +29,8 @@ from zope.dublincore.zopedublincore import ScalarProperty
from zope.component import adapts from zope.component import adapts
from zope.interface import implements from zope.interface import implements
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from cybertools.storage.interfaces import IStorageInfo
from loops.interfaces import ILoopsObject, ILoopsContained, IConcept, IResource from loops.interfaces import ILoopsObject, ILoopsContained, IConcept, IResource
from loops.interfaces import IResourceAdapter from loops.interfaces import IResourceAdapter
@ -72,10 +74,15 @@ class AdapterBase(object):
class ResourceAdapterBase(AdapterBase): class ResourceAdapterBase(AdapterBase):
implements(IStorageInfo)
adapts(IResource) adapts(IResource)
_adapterAttributes = ('storageName', 'storageParams', ) + AdapterBase._adapterAttributes
_contextAttributes = list(IResourceAdapter) _contextAttributes = list(IResourceAdapter)
storageName = None
storageParams = None
# other adapters # other adapters

View file

@ -344,7 +344,9 @@
set_schema="loops.query.IQueryConcept" /> set_schema="loops.query.IQueryConcept" />
</class> </class>
<adapter factory="loops.resource.FileAdapter" trusted="True" /> <adapter factory="loops.resource.FileAdapter"
provides="loops.interfaces.IFile"
trusted="True" />
<class class="loops.resource.FileAdapter"> <class class="loops.resource.FileAdapter">
<require permission="zope.View" <require permission="zope.View"
interface="loops.interfaces.IFile" /> interface="loops.interfaces.IFile" />
@ -361,7 +363,9 @@
set_schema="loops.interfaces.IFile" /> set_schema="loops.interfaces.IFile" />
</class> </class>
<adapter factory="loops.resource.NoteAdapter" trusted="True" /> <adapter factory="loops.resource.NoteAdapter"
provides="loops.interfaces.INote"
trusted="True" />
<class class="loops.resource.NoteAdapter"> <class class="loops.resource.NoteAdapter">
<require permission="zope.View" <require permission="zope.View"
interface="loops.interfaces.INote" /> interface="loops.interfaces.INote" />

View file

@ -207,6 +207,10 @@ Now let's have a look at resources.
Using the type machinery we can also specify options that may be used Using the type machinery we can also specify options that may be used
for controlling e.g. storage for external files. for controlling e.g. storage for external files.
>>> from loops.interfaces import IFile
>>> from loops.resource import FileAdapter
>>> component.provideAdapter(FileAdapter, provides=IFile)
>>> extfile = concepts['extfile'] = Concept(u'External File') >>> extfile = concepts['extfile'] = Concept(u'External File')
>>> ef1 = resources['ef1'] = Resource(u'Extfile #1') >>> ef1 = resources['ef1'] = Resource(u'Extfile #1')
>>> ef1.resourceType = extfile >>> ef1.resourceType = extfile

View file

@ -41,6 +41,7 @@ from loops.browser.concept import ConceptRelationView
from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager
from loops.organize.interfaces import IMemberRegistration from loops.organize.interfaces import IMemberRegistration
from loops.organize.party import getPersonForUser from loops.organize.party import getPersonForUser
import loops.browser.util
_ = MessageFactory('zope') _ = MessageFactory('zope')
@ -75,11 +76,23 @@ class PasswordWidget(BasePasswordWidget):
class MemberRegistration(Form, NodeView): class MemberRegistration(Form, NodeView):
form_fields = FormFields(IMemberRegistration).omit('age') form_fields = FormFields(IMemberRegistration).omit('age')
template = NamedTemplate('loops.dataform') template = loops.browser.util.dataform
label = _(u'Member Registration') label = _(u'Member Registration')
def __init__(self, context, request): def __init__(self, context, request):
NodeView.__init__(self, context, request) #NodeView.__init__(self, context, request)
super(MemberRegistration, self).__init__(context, request)
@Lazy
def macro(self):
return self.template.macros['content']
@Lazy
def item(self):
return self
def __call__(self, *args, **kw):
return NodeView.__call__(self, *args, **kw)
@action(_(u'Register')) @action(_(u'Register'))
def handle_register_action(self, action, data): def handle_register_action(self, action, data):

View file

@ -77,10 +77,12 @@
interface="loops.organize.interfaces.IMemberRegistrationManager" /> interface="loops.organize.interfaces.IMemberRegistrationManager" />
</zope:class> </zope:class>
<browser:page name="registration.html" <browser:page
for="loops.interfaces.IView" for="loops.interfaces.INode"
name="register_user.html"
class="loops.organize.browser.MemberRegistration" class="loops.organize.browser.MemberRegistration"
permission="zope.Public" /> permission="zope.Public"
/>
<zope:view <zope:view
type="zope.publisher.interfaces.browser.IBrowserRequest" type="zope.publisher.interfaces.browser.IBrowserRequest"

View file

@ -34,6 +34,7 @@ from zope.component import adapts
from zope.i18nmessageid import MessageFactory from zope.i18nmessageid import MessageFactory
from zope.interface import implements from zope.interface import implements
from zope.size.interfaces import ISized from zope.size.interfaces import ISized
from zope.security.proxy import removeSecurityProxy
from zope.traversing.api import getName, getParent from zope.traversing.api import getName, getParent
from persistent import Persistent from persistent import Persistent
from cStringIO import StringIO from cStringIO import StringIO
@ -52,6 +53,7 @@ from loops.interfaces import IFile, IExternalFile, INote
from loops.interfaces import IDocument, ITextDocument, IDocumentSchema, IDocumentView from loops.interfaces import IDocument, ITextDocument, IDocumentSchema, IDocumentView
from loops.interfaces import IMediaAsset, IMediaAssetView from loops.interfaces import IMediaAsset, IMediaAssetView
from loops.interfaces import IResourceManager, IResourceManagerContained from loops.interfaces import IResourceManager, IResourceManagerContained
from loops.interfaces import ITypeConcept
from loops.interfaces import ILoopsContained from loops.interfaces import ILoopsContained
from loops.interfaces import IIndexAttributes from loops.interfaces import IIndexAttributes
from loops.concept import ResourceRelation from loops.concept import ResourceRelation
@ -77,10 +79,13 @@ class Resource(Image, Contained):
# TODO: remove dependency on Image # TODO: remove dependency on Image
implements(IBaseResource, IResource, IResourceManagerContained, IRelatable, ISized) implements(IBaseResource, IResource, IResourceManagerContained,
IRelatable, ISized)
proxyInterface = IMediaAssetView # obsolete! proxyInterface = IMediaAssetView # obsolete!
storageName = None
_size = _width = _height = 0 _size = _width = _height = 0
def __init__(self, title=u''): def __init__(self, title=u''):
@ -106,10 +111,16 @@ class Resource(Image, Contained):
# TODO (?): check for multiple types (->Error) # TODO (?): check for multiple types (->Error)
return concepts and concepts[0] or cm.get('file', None) return concepts and concepts[0] or cm.get('file', None)
def setResourceType(self, concept): def setResourceType(self, concept):
if concept is None: if concept is None: # this should not happen
return return
current = self.getResourceType() current = self.getResourceType()
if current != concept: if current != concept:
# change storage if necessary, and migrate data
oldType = IType(self)
from loops.type import ConceptTypeInfo
newType = ConceptTypeInfo(concept)
self.migrateStorage(oldType, newType)
# assign new type parent
typePred = self.getLoopsRoot().getConceptManager().getTypePredicate() typePred = self.getLoopsRoot().getConceptManager().getTypePredicate()
if typePred is None: if typePred is None:
raise ValueError('No type predicate found for ' + getName(self)) raise ValueError('No type predicate found for ' + getName(self))
@ -122,6 +133,8 @@ class Resource(Image, Contained):
return self.resourceType return self.resourceType
def _setData(self, data): def _setData(self, data):
#if not data:
# return
dataFile = StringIO(data) # let File tear it into pieces dataFile = StringIO(data) # let File tear it into pieces
super(Resource, self)._setData(dataFile) super(Resource, self)._setData(dataFile)
if not self.contentType: if not self.contentType:
@ -130,6 +143,7 @@ class Resource(Image, Contained):
data = property(Image._getData, _setData) data = property(Image._getData, _setData)
def guessContentType(self, data): def guessContentType(self, data):
# probably obsolete, use zope.contenttype.guess_content_type()
if not isinstance(data, str): # seems to be a file object if not isinstance(data, str): # seems to be a file object
data = data.read(20) data = data.read(20)
if data.startswith('%PDF'): if data.startswith('%PDF'):
@ -204,6 +218,32 @@ class Resource(Image, Contained):
return '%.1f %s' % (size, unit) return '%.1f %s' % (size, unit)
#return '%s %s' % (util.getNiceNumber(size), unit) #return '%s %s' % (util.getNiceNumber(size), unit)
# storage migration
def migrateStorage(self, oldType, newType):
oldType = removeSecurityProxy(oldType)
newType = removeSecurityProxy(newType)
context = removeSecurityProxy(self)
oldAdapted = newAdapted = context
oldTi = removeSecurityProxy(oldType.typeInterface)
if oldTi is not None:
oldAdapted = oldTi(context)
newTi = removeSecurityProxy(newType.typeInterface)
newOptions = {}
if newTi is not None:
newAdapted = newTi(context)
# make sure we use options of new type:
newOptions = newType.optionsDict
object.__setattr__(newAdapted, 'options', newOptions)
#print 'migrateStorage:', newAdapted, newOptions, oldAdapted, oldAdapted.storageName
if newOptions.get('storage') != oldAdapted.storageName:
data = oldAdapted.data
#print 'data', data
oldAdapted.data = '' # clear old storage
context._storageName = None # let's take storage from new type options
context._storageParams = None # "
newAdapted.data = data
# Document and MediaAsset are legacy classes, will become obsolete # Document and MediaAsset are legacy classes, will become obsolete
@ -240,15 +280,41 @@ class FileAdapter(ResourceAdapterBase):
_contextAttributes = list(IFile) + list(IBaseResource) _contextAttributes = list(IFile) + list(IBaseResource)
_adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',) _adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',)
def setData(self, data): self.context.data = data def setData(self, data):
#if self.storageName is None:
# self.storageName = 'zopefile'
self.storageName = None
self.context.data = data
def getData(self): return self.context.data def getData(self): return self.context.data
data = property(getData, setData) data = property(getData, setData)
@Lazy
def options(self):
return IType(self.context).optionsDict
def getStorageName(self):
return (getattr(self.context, '_storageName', None)
or self.options.get('storage', None))
def setStorageName(self, value):
self.context._storageName = value
storageName = property(getStorageName, setStorageName)
class ExternalFileAdapter(FileAdapter): class ExternalFileAdapter(FileAdapter):
implements(IExternalFile) implements(IExternalFile)
def getStorageParams(self):
params = getattr(self.context, '_storageParams', None)
if params is not None:
return params
else:
value = self.options.get('storage_parameters') or 'extfiles'
return dict(subdirectory=value)
def setStorageParams(self, value):
self.context._storageParams = value
storageParams = property(getStorageParams, setStorageParams)
@Lazy @Lazy
def externalAddress(self): def externalAddress(self):
# or is this an editable attribute? # or is this an editable attribute?
@ -256,23 +322,17 @@ class ExternalFileAdapter(FileAdapter):
# anyway: an attribute of the context object. # anyway: an attribute of the context object.
return self.context.__name__ 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):
params = self.options.get('storage_parameters') or 'extfiles'
return dict(subdirectory=params)
def setData(self, data): def setData(self, data):
storage = component.getUtility(IExternalStorage, name=self.storageName) if not data:
storage.setData(self.externalAddress, data, params=self.storageParams) return
storageParams = self.storageParams
storageName = self.storageName
storage = component.getUtility(IExternalStorage, name=storageName)
storage.setData(self.externalAddress, data, params=storageParams)
self.context._size = len(data) self.context._size = len(data)
# remember storage settings:
self.storageParams = storageParams
self.storageName = storageName
def getData(self): def getData(self):
storage = component.getUtility(IExternalStorage, name=self.storageName) storage = component.getUtility(IExternalStorage, name=self.storageName)

View file

@ -54,6 +54,7 @@
tal:condition="view/useVersioning">V</th> tal:condition="view/useVersioning">V</th>
<th i18n:translate="label_size">Size</th> <th i18n:translate="label_size">Size</th>
<th i18n:translate="label_modifdate">Modification Date</th> <th i18n:translate="label_modifdate">Modification Date</th>
<th i18n:translate="label_authors">Author(s)</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -80,12 +81,7 @@
<span tal:replace="row/context/sizeForDisplay|string:">Size</span> <span tal:replace="row/context/sizeForDisplay|string:">Size</span>
</td> </td>
<td><span tal:replace="row/modified">modified</span></td> <td><span tal:replace="row/modified">modified</span></td>
</tr> <td><span tal:replace="row/creators">John</span></td>
<tr tal:condition="nothing"
tal:attributes="class class">
<td colspan="3" style="padding-left: 7em">
<i tal:content="description">describing...</i>
</td>
</tr> </tr>
</tal:item> </tal:item>
</tal:items> </tal:items>

View file

@ -90,6 +90,7 @@ class SetupManager(object):
#ITypeConcept(image).typeInterface = IImage #ITypeConcept(image).typeInterface = IImage
ITypeConcept(textdocument).typeInterface = ITextDocument ITypeConcept(textdocument).typeInterface = ITextDocument
ITypeConcept(note).typeInterface = INote ITypeConcept(note).typeInterface = INote
#ITypeConcept(note).viewName = 'note.html'
hasType.conceptType = predicate hasType.conceptType = predicate
standard.conceptType = predicate standard.conceptType = predicate

View file

@ -141,8 +141,10 @@ Resources
--------- ---------
>>> from loops.resource import TextDocumentAdapter >>> from loops.resource import TextDocumentAdapter
>>> from loops.interfaces import IResource, ITextDocument >>> from loops.interfaces import IResource, ITextDocument, IFile
>>> component.provideAdapter(TextDocumentAdapter, (IResource,), ITextDocument) >>> component.provideAdapter(TextDocumentAdapter, (IResource,), ITextDocument)
>>> from loops.resource import FileAdapter
>>> component.provideAdapter(FileAdapter, provides=IFile)
>>> zope3Id = xrf.getObjectByName('zope3')['id'] >>> zope3Id = xrf.getObjectByName('zope3')['id']
>>> td01 = resources['td01'] = Resource(u'Doc1') >>> td01 = resources['td01'] = Resource(u'Doc1')