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" 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 --> <!-- inner HTML views -->
<page <page

View file

@ -41,8 +41,8 @@ from zope.security.proxy import isinstance, removeSecurityProxy
from cybertools.ajax import innerHtml from cybertools.ajax import innerHtml
from cybertools.browser.form import FormController from cybertools.browser.form import FormController
from cybertools.composer.interfaces import IInstance 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.browser.common import schema_macros, schema_edit_macros
from cybertools.composer.schema.util import getSchemaFromInterface
from cybertools.typology.interfaces import IType, ITypeManager from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted from loops.common import adapted
from loops.concept import Concept, ResourceRelation from loops.concept import Concept, ResourceRelation
@ -58,26 +58,6 @@ from loops.util import _
from loops.versioning.interfaces import IVersionable 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 # forms
class ObjectForm(NodeView): class ObjectForm(NodeView):
@ -91,7 +71,13 @@ class ObjectForm(NodeView):
def __init__(self, context, request): def __init__(self, context, request):
super(ObjectForm, self).__init__(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 @property
def schemaMacros(self): def schemaMacros(self):
@ -103,10 +89,13 @@ class ObjectForm(NodeView):
@Lazy @Lazy
def schema(self): 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 @Lazy
def fields(self): def fields(self):
return self.schema.fields
fields = self.schema.fields fields = self.schema.fields
fields.data.height = 10 fields.data.height = 10
ifc = self.typeInterface ifc = self.typeInterface
@ -130,41 +119,12 @@ class ObjectForm(NodeView):
def instance(self): def instance(self):
return IInstance(adapted(self.context)) 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): def __call__(self):
response = self.request.response response = self.request.response
response.setHeader('Expires', 'Sat, 1 Jan 2000 00:00:00 GMT') response.setHeader('Expires', 'Sat, 1 Jan 2000 00:00:00 GMT')
response.setHeader('Pragma', 'no-cache') response.setHeader('Pragma', 'no-cache')
return innerHtml(self) return innerHtml(self)
@Lazy
def typeInterface(self):
return IType(self.context).typeInterface or ITextDocument
@Lazy @Lazy
def defaultPredicate(self): def defaultPredicate(self):
return self.loopsRoot.getConceptManager().getDefaultPredicate() return self.loopsRoot.getConceptManager().getDefaultPredicate()
@ -194,55 +154,6 @@ class ObjectForm(NodeView):
for o in result]) 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): class EditObjectForm(ObjectForm, EditForm):
@property @property
@ -275,13 +186,13 @@ class CreateObjectForm(ObjectForm, Form):
form_action = 'create_resource' form_action = 'create_resource'
dialog_name = 'create' dialog_name = 'create'
# cybertools.composer.schema support @Lazy
def adapted(self):
return self.typeInterface(Resource())
@Lazy @Lazy
def instance(self): def instance(self):
return IInstance(Concept()) return IInstance(Resource())
# general methods
@Lazy @Lazy
def typeInterface(self): def typeInterface(self):
@ -307,13 +218,16 @@ class CreateObjectForm(ObjectForm, Form):
class InnerForm(CreateObjectForm): class InnerForm(CreateObjectForm):
@property @property
#def macro(self): return self.template.macros['fields']
def macro(self): return self.schemaMacros['fields'] def macro(self): return self.schemaMacros['fields']
# processing form input # processing form input
class EditObject(FormController): 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.' prefix = 'form.'
conceptPrefix = 'assignments.' conceptPrefix = 'assignments.'
@ -321,9 +235,18 @@ class EditObject(FormController):
old = None old = None
selected = None selected = None
@Lazy
def adapted(self):
return adapted(self.object)
@Lazy
def typeInterface(self):
return IType(self.object).typeInterface or ITextDocument
@Lazy @Lazy
def schema(self): def schema(self):
return getSchemaFromInterface(self.typeInterface, manager=self) schemaFactory = component.getAdapter(self.adapted, ISchemaFactory)
return schemaFactory(self.typeInterface)
@Lazy @Lazy
def fields(self): def fields(self):
@ -331,12 +254,7 @@ class EditObject(FormController):
@Lazy @Lazy
def instance(self): def instance(self):
return component.getAdapter(adapted(self.object), IInstance, name='editor') return component.getAdapter(self.adapted, IInstance, name='editor')
#return IInstance(adapted(self.object), name='editor')
@Lazy
def typeInterface(self):
return IType(self.object).typeInterface or ITextDocument
@Lazy @Lazy
def loopsRoot(self): def loopsRoot(self):
@ -398,50 +316,6 @@ class EditObject(FormController):
def fieldHandlers(self): def fieldHandlers(self):
return dict(fileupload=self.handleFileUpload) 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): def collectConcepts(self, fieldName, value):
if self.old is None: self.old = [] if self.old is None: self.old = []
if self.selected is None: self.selected = [] if self.selected is None: self.selected = []

View file

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

View file

@ -375,8 +375,8 @@ class TextDocumentAdapter(DocumentAdapter):
""" A type adapter for providing text document functionality for resources. """ A type adapter for providing text document functionality for resources.
""" """
implements(IDocument) implements(ITextDocument)
_contextAttributes = list(IDocument) + list(IBaseResource) _contextAttributes = list(ITextDocument) + list(IBaseResource)
class NoteAdapter(DocumentAdapter): 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.annotatableadapter import ZDCAnnotatableAdapter
from zope.dublincore.interfaces import IZopeDublinCore 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.field import FieldInstance, NumberFieldInstance
from cybertools.composer.schema.instance import Instance, Editor from cybertools.composer.schema.instance import Instance, Editor
from cybertools.relation.tests import IntIdsStub from cybertools.relation.tests import IntIdsStub
@ -34,6 +35,7 @@ from loops.concept import Concept
from loops.concept import IndexAttributes as ConceptIndexAttributes from loops.concept import IndexAttributes as ConceptIndexAttributes
from loops.resource import Resource, FileAdapter from loops.resource import Resource, FileAdapter
from loops.resource import IndexAttributes as ResourceIndexAttributes from loops.resource import IndexAttributes as ResourceIndexAttributes
from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFactory
from loops.setup import SetupManager, addObject from loops.setup import SetupManager, addObject
from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept
@ -67,6 +69,11 @@ class TestSite(object):
component.provideAdapter(FieldInstance) component.provideAdapter(FieldInstance)
component.provideAdapter(NumberFieldInstance, name='number') component.provideAdapter(NumberFieldInstance, name='number')
component.provideAdapter(SchemaFactory)
component.provideAdapter(ResourceSchemaFactory)
component.provideAdapter(FileSchemaFactory)
component.provideAdapter(NoteSchemaFactory)
component.getSiteManager().registerHandler(invalidateRelations, component.getSiteManager().registerHandler(invalidateRelations,
(ILoopsObject, IObjectRemovedEvent)) (ILoopsObject, IObjectRemovedEvent))
component.getSiteManager().registerHandler(removeRelation, component.getSiteManager().registerHandler(removeRelation,