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.resource import NoteAdapter
>>> component.provideAdapter(TypeConcept)
>>> component.provideAdapter(NoteAdapter)
>>> component.provideAdapter(NoteAdapter, provides=INote)
>>> note_tc = concepts['note'] = Concept('Note')
>>> note_tc.conceptType = typeObject
>>> ITypeConcept(note_tc).typeInterface = INote

View file

@ -25,6 +25,7 @@ $Id$
from zope.app import zapi
from zope import component
from zope.app.form.browser.interfaces import ITerms
from zope.app.security.interfaces import IAuthentication
from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve
from zope.dublincore.interfaces import IZopeDublinCore
@ -118,6 +119,17 @@ class BaseView(GenericView):
d = dc.modified or dc.created
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
def loopsRoot(self):
return self.context.getLoopsRoot()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -123,7 +123,8 @@
</div><br />
<div tal:repeat="item item/pageItems">
<a href="#"
tal:attributes="href item/url"
tal:attributes="href item/url;
title item/description"
tal:content="item/title">Item</a>
</div>
</metal:body>
@ -142,13 +143,9 @@
tal:condition="nocall:target">
<div tal:repeat="related item/resources">
<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>
<div style="margin-left: 5em"
tal:define="description related/description"
tal:condition="description">
<i tal:content="description">Description</i>
</div>
</div>
</div>
</metal:resources>
@ -169,13 +166,9 @@
tal:condition="nocall:target">
<div tal:repeat="related item/children">
<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>
<div style="margin-left: 5em"
tal:define="description related/description"
tal:condition="description">
<i tal:content="description">Description</i>
</div>
</div>
</div>
</metal:children>

View file

@ -27,11 +27,13 @@ from zope import component
from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog
from zope.dublincore.interfaces import ICMFDublinCore
from zope.app.form.browser.textwidgets import FileWidget
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.formlib.form import FormFields
from zope.formlib.interfaces import DISPLAY_UNWRITEABLE
from zope.proxy import removeAllProxies
from zope.schema.interfaces import IBytes
from zope.security import canAccess, canWrite
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):
@Lazy
@ -67,6 +77,9 @@ class ResourceEditForm(EditForm):
if typeInterface is not None:
omit = [f for f in typeInterface if f in IBaseResource]
fields = FormFields(fields.omit(*omit), typeInterface)
dataField = fields['data']
if IBytes.providedBy(dataField):
dataField.customWidget = CustomFileWidget
return fields
def setUpWidgets(self, ignore_request=False):

View file

@ -30,7 +30,8 @@ from zope.formlib.namedtemplate import NamedTemplateImplementation
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'))
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.interface import implements
from zope.cachedescriptors.property import Lazy
from cybertools.storage.interfaces import IStorageInfo
from loops.interfaces import ILoopsObject, ILoopsContained, IConcept, IResource
from loops.interfaces import IResourceAdapter
@ -72,10 +74,15 @@ class AdapterBase(object):
class ResourceAdapterBase(AdapterBase):
implements(IStorageInfo)
adapts(IResource)
_adapterAttributes = ('storageName', 'storageParams', ) + AdapterBase._adapterAttributes
_contextAttributes = list(IResourceAdapter)
storageName = None
storageParams = None
# other adapters

View file

@ -344,7 +344,9 @@
set_schema="loops.query.IQueryConcept" />
</class>
<adapter factory="loops.resource.FileAdapter" trusted="True" />
<adapter factory="loops.resource.FileAdapter"
provides="loops.interfaces.IFile"
trusted="True" />
<class class="loops.resource.FileAdapter">
<require permission="zope.View"
interface="loops.interfaces.IFile" />
@ -361,7 +363,9 @@
set_schema="loops.interfaces.IFile" />
</class>
<adapter factory="loops.resource.NoteAdapter" trusted="True" />
<adapter factory="loops.resource.NoteAdapter"
provides="loops.interfaces.INote"
trusted="True" />
<class class="loops.resource.NoteAdapter">
<require permission="zope.View"
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
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')
>>> ef1 = resources['ef1'] = Resource(u'Extfile #1')
>>> 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 IMemberRegistration
from loops.organize.party import getPersonForUser
import loops.browser.util
_ = MessageFactory('zope')
@ -75,11 +76,23 @@ class PasswordWidget(BasePasswordWidget):
class MemberRegistration(Form, NodeView):
form_fields = FormFields(IMemberRegistration).omit('age')
template = NamedTemplate('loops.dataform')
template = loops.browser.util.dataform
label = _(u'Member Registration')
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'))
def handle_register_action(self, action, data):

View file

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

View file

@ -34,6 +34,7 @@ from zope.component import adapts
from zope.i18nmessageid import MessageFactory
from zope.interface import implements
from zope.size.interfaces import ISized
from zope.security.proxy import removeSecurityProxy
from zope.traversing.api import getName, getParent
from persistent import Persistent
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 IMediaAsset, IMediaAssetView
from loops.interfaces import IResourceManager, IResourceManagerContained
from loops.interfaces import ITypeConcept
from loops.interfaces import ILoopsContained
from loops.interfaces import IIndexAttributes
from loops.concept import ResourceRelation
@ -77,10 +79,13 @@ class Resource(Image, Contained):
# TODO: remove dependency on Image
implements(IBaseResource, IResource, IResourceManagerContained, IRelatable, ISized)
implements(IBaseResource, IResource, IResourceManagerContained,
IRelatable, ISized)
proxyInterface = IMediaAssetView # obsolete!
storageName = None
_size = _width = _height = 0
def __init__(self, title=u''):
@ -106,10 +111,16 @@ class Resource(Image, Contained):
# TODO (?): check for multiple types (->Error)
return concepts and concepts[0] or cm.get('file', None)
def setResourceType(self, concept):
if concept is None:
if concept is None: # this should not happen
return
current = self.getResourceType()
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()
if typePred is None:
raise ValueError('No type predicate found for ' + getName(self))
@ -122,6 +133,8 @@ class Resource(Image, Contained):
return self.resourceType
def _setData(self, data):
#if not data:
# return
dataFile = StringIO(data) # let File tear it into pieces
super(Resource, self)._setData(dataFile)
if not self.contentType:
@ -130,6 +143,7 @@ class Resource(Image, Contained):
data = property(Image._getData, _setData)
def guessContentType(self, data):
# probably obsolete, use zope.contenttype.guess_content_type()
if not isinstance(data, str): # seems to be a file object
data = data.read(20)
if data.startswith('%PDF'):
@ -204,6 +218,32 @@ class Resource(Image, Contained):
return '%.1f %s' % (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
@ -240,15 +280,41 @@ class FileAdapter(ResourceAdapterBase):
_contextAttributes = list(IFile) + list(IBaseResource)
_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
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):
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
def externalAddress(self):
# or is this an editable attribute?
@ -256,23 +322,17 @@ class ExternalFileAdapter(FileAdapter):
# 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):
params = self.options.get('storage_parameters') or 'extfiles'
return dict(subdirectory=params)
def setData(self, data):
storage = component.getUtility(IExternalStorage, name=self.storageName)
storage.setData(self.externalAddress, data, params=self.storageParams)
if not data:
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)
# remember storage settings:
self.storageParams = storageParams
self.storageName = storageName
def getData(self):
storage = component.getUtility(IExternalStorage, name=self.storageName)

View file

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

View file

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

View file

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