use new SchemaFactory for fine-grained control of field properties

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2080 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-09-30 17:12:55 +00:00
parent c2807ce730
commit 5fd6f75a60
6 changed files with 107 additions and 168 deletions

View file

@ -580,14 +580,6 @@
permission="zope.ManageContent"
/>
<zope:view
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="loops.interfaces.IFile"
provides="zope.app.form.interfaces.IInputWidget"
factory="loops.browser.form.UploadWidget"
permission="zope.Public"
/>
<!-- inner HTML views -->
<page

View file

@ -41,8 +41,8 @@ from zope.security.proxy import isinstance, removeSecurityProxy
from cybertools.ajax import innerHtml
from cybertools.browser.form import FormController
from cybertools.composer.interfaces import IInstance
from cybertools.composer.schema.interfaces import ISchemaFactory
from cybertools.composer.schema.browser.common import schema_macros, schema_edit_macros
from cybertools.composer.schema.util import getSchemaFromInterface
from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted
from loops.concept import Concept, ResourceRelation
@ -58,26 +58,6 @@ from loops.util import _
from loops.versioning.interfaces import IVersionable
# special widgets
class UploadWidget(FileWidget):
def _toFieldValue(self, input):
# not used at the moment as the context object is updated
# via EditObject.updateFields()
fn = getattr(input, 'filename', '') # zope.publisher.browser.FileUpload
self.request.form['filename'] = fn
if input:
self.request.form['_tempfilename'] = input.headers.get('_tempfilename')
# f = self.context
# f.extfiledata = tempfilename # provide for rename
if fn:
contentType = guess_content_type(fn)
if contentType:
request.form['form.contentType'] = contentType
return super(UploadWidget, self)._toFieldValue(input)
# forms
class ObjectForm(NodeView):
@ -91,7 +71,13 @@ class ObjectForm(NodeView):
def __init__(self, context, request):
super(ObjectForm, self).__init__(context, request)
# cybertools.composer.schema support
@Lazy
def adapted(self):
return adapted(self.context)
@Lazy
def typeInterface(self):
return IType(self.context).typeInterface or ITextDocument
@property
def schemaMacros(self):
@ -103,10 +89,13 @@ class ObjectForm(NodeView):
@Lazy
def schema(self):
return getSchemaFromInterface(self.typeInterface, manager=self)
schemaFactory = component.getAdapter(self.adapted, ISchemaFactory)
return schemaFactory(self.typeInterface, manager=self,
request=self.request)
@Lazy
def fields(self):
return self.schema.fields
fields = self.schema.fields
fields.data.height = 10
ifc = self.typeInterface
@ -130,41 +119,12 @@ class ObjectForm(NodeView):
def instance(self):
return IInstance(adapted(self.context))
# zope.formlib support
@property
def form_fields(self):
ifc = self.typeInterface
ff = FormFields(ifc)
if ifc in widgetControllers:
wc = widgetControllers[ifc](self.context, self.request)
ff = wc.modifyFormFields(ff)
return ff
def setUp(self):
if self._isSetUp:
return
self.setUpWidgets()
desc = self.widgets.get('description')
if desc:
desc.height = 2
if self.typeInterface in widgetControllers:
wc = widgetControllers[self.typeInterface](self.context, self.request)
wc.modifyWidgetSetup(self.widgets)
self._isSetUp = True
# general methods
def __call__(self):
response = self.request.response
response.setHeader('Expires', 'Sat, 1 Jan 2000 00:00:00 GMT')
response.setHeader('Pragma', 'no-cache')
return innerHtml(self)
@Lazy
def typeInterface(self):
return IType(self.context).typeInterface or ITextDocument
@Lazy
def defaultPredicate(self):
return self.loopsRoot.getConceptManager().getDefaultPredicate()
@ -194,55 +154,6 @@ class ObjectForm(NodeView):
for o in result])
class WidgetController(object):
def __init__(self, context, request):
self.context = context
self.request = request
def modifySchemaFields(self, fields):
pass
def modifyFormFields(self, formFields):
return formFields
def modifyWidgetSetup(self, widgets):
pass
class NoteWidgetController(WidgetController):
def modifySchemaFields(self, fields):
del fields['description']
fields.data.height = 5
def modifyFormFields(self, formFields):
return formFields.omit('description')
def modifyWidgetSetup(self, widgets):
widgets['data'].height = 5
class FileWidgetController(WidgetController):
def modifySchemaFields(self, fields):
if self.request.principal.id != 'rootadmin':
del fields['contentType']
def modifyFormFields(self, formFields):
if self.request.principal.id == 'rootadmin':
return formFields
return formFields.omit('contentType')
widgetControllers = {
INote: NoteWidgetController,
IFile: FileWidgetController,
IExternalFile: FileWidgetController,
}
class EditObjectForm(ObjectForm, EditForm):
@property
@ -275,13 +186,13 @@ class CreateObjectForm(ObjectForm, Form):
form_action = 'create_resource'
dialog_name = 'create'
# cybertools.composer.schema support
@Lazy
def adapted(self):
return self.typeInterface(Resource())
@Lazy
def instance(self):
return IInstance(Concept())
# general methods
return IInstance(Resource())
@Lazy
def typeInterface(self):
@ -307,13 +218,16 @@ class CreateObjectForm(ObjectForm, Form):
class InnerForm(CreateObjectForm):
@property
#def macro(self): return self.template.macros['fields']
def macro(self): return self.schemaMacros['fields']
# processing form input
class EditObject(FormController):
""" Note that ``self.context`` of this adapter may be different from
``self.object``, the object it acts upon, e.g. when this object
is created during the update processing.
"""
prefix = 'form.'
conceptPrefix = 'assignments.'
@ -321,9 +235,18 @@ class EditObject(FormController):
old = None
selected = None
@Lazy
def adapted(self):
return adapted(self.object)
@Lazy
def typeInterface(self):
return IType(self.object).typeInterface or ITextDocument
@Lazy
def schema(self):
return getSchemaFromInterface(self.typeInterface, manager=self)
schemaFactory = component.getAdapter(self.adapted, ISchemaFactory)
return schemaFactory(self.typeInterface)
@Lazy
def fields(self):
@ -331,12 +254,7 @@ class EditObject(FormController):
@Lazy
def instance(self):
return component.getAdapter(adapted(self.object), IInstance, name='editor')
#return IInstance(adapted(self.object), name='editor')
@Lazy
def typeInterface(self):
return IType(self.object).typeInterface or ITextDocument
return component.getAdapter(self.adapted, IInstance, name='editor')
@Lazy
def loopsRoot(self):
@ -398,50 +316,6 @@ class EditObject(FormController):
def fieldHandlers(self):
return dict(fileupload=self.handleFileUpload)
def xupdateFields(self): # obsolete
obj = self.object
errors = {}
form = self.request.form
ti = IType(obj).typeInterface
if ti is not None:
adapted = ti(obj)
else:
adapted = obj
for k in form.keys():
# TODO: use self.view.form_fields or better: IInstance(adapted)
if k.startswith(self.prefix):
fn = k[len(self.prefix):]
if fn in ('action', 'type', 'data.used') or fn.endswith('-empty-marker'):
continue
value = form[k]
if fn.startswith(self.conceptPrefix) and value:
self.collectConcepts(fn[len(self.conceptPrefix):], value)
else:
if not value and fn == 'data' and IFile.providedBy(adapted):
# empty file data - don't change
continue
if isinstance(value, FileUpload):
filename = getattr(value, 'filename', '')
value = value.read()
if filename:
#self.request.form['filename'] = filename
contentType = guess_content_type(filename, value[:100])
if contentType:
ct = contentType[0]
self.request.form['form.contentType'] = ct
adapted.contentType = ct
adapted.localFilename = filename
if fn == 'title' and not value:
# TODO: provide general validation mechanism
errors[fn] = 'Field %s must not be empty' % fn
else:
# TODO: provide unmarshalling depending on field type
setattr(adapted, fn, value)
if self.old or self.selected:
self.assignConcepts(obj)
notify(ObjectModifiedEvent(obj))
return errors
def collectConcepts(self, fieldName, value):
if self.old is None: self.old = []
if self.selected is None: self.selected = []

View file

@ -392,7 +392,6 @@
interface="zope.filerepresentation.interfaces.IFileFactory" />
</class>
<adapter factory="cybertools.composer.schema.instance.Instance" />
<adapter factory="cybertools.composer.schema.instance.Editor"
name="editor" />
@ -402,6 +401,11 @@
<adapter factory="cybertools.composer.schema.field.FileUploadFieldInstance"
name="fileupload" />
<adapter factory="loops.schema.ResourceSchemaFactory" />
<adapter factory="loops.schema.ResourceSchemaFactory"
for="loops.interfaces.IResource" />
<adapter factory="loops.schema.FileSchemaFactory" />
<adapter factory="loops.schema.NoteSchemaFactory" />
<adapter factory="loops.setup.SetupManager" />
<adapter factory="loops.external.NodesLoader" />

View file

@ -375,8 +375,8 @@ class TextDocumentAdapter(DocumentAdapter):
""" A type adapter for providing text document functionality for resources.
"""
implements(IDocument)
_contextAttributes = list(IDocument) + list(IBaseResource)
implements(ITextDocument)
_contextAttributes = list(ITextDocument) + list(IBaseResource)
class NoteAdapter(DocumentAdapter):

62
schema.py Normal file
View file

@ -0,0 +1,62 @@
#
# Copyright (c) 2005 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
#
"""
Specialized schema factories
$Id$
"""
from zope.component import adapts
from cybertools.composer.schema.factory import SchemaFactory
from loops.interfaces import IResourceAdapter, IFile, INote
class ResourceSchemaFactory(SchemaFactory):
adapts(IResourceAdapter)
def __call__(self, interface, **kw):
schema = super(ResourceSchemaFactory, self).__call__(interface, **kw)
schema.fields.data.height = 10
return schema
class FileSchemaFactory(SchemaFactory):
adapts(IFile)
def __call__(self, interface, **kw):
schema = super(FileSchemaFactory, self).__call__(interface, **kw)
if 'request' in kw and kw['request'].principal.id != 'rootadmin':
del schema.fields['contentType']
return schema
class NoteSchemaFactory(SchemaFactory):
adapts(INote)
def __call__(self, interface, **kw):
schema = super(NoteSchemaFactory, self).__call__(interface, **kw)
del schema.fields['description']
schema.fields.data.height = 5
return schema

View file

@ -16,6 +16,7 @@ from zope.app.security.interfaces import IAuthentication
from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter
from zope.dublincore.interfaces import IZopeDublinCore
from cybertools.composer.schema.factory import SchemaFactory
from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
from cybertools.composer.schema.instance import Instance, Editor
from cybertools.relation.tests import IntIdsStub
@ -34,6 +35,7 @@ from loops.concept import Concept
from loops.concept import IndexAttributes as ConceptIndexAttributes
from loops.resource import Resource, FileAdapter
from loops.resource import IndexAttributes as ResourceIndexAttributes
from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFactory
from loops.setup import SetupManager, addObject
from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept
@ -67,6 +69,11 @@ class TestSite(object):
component.provideAdapter(FieldInstance)
component.provideAdapter(NumberFieldInstance, name='number')
component.provideAdapter(SchemaFactory)
component.provideAdapter(ResourceSchemaFactory)
component.provideAdapter(FileSchemaFactory)
component.provideAdapter(NoteSchemaFactory)
component.getSiteManager().registerHandler(invalidateRelations,
(ILoopsObject, IObjectRemovedEvent))
component.getSiteManager().registerHandler(removeRelation,