--- a/web/form.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/form.py Tue Sep 29 12:45:23 2009 +0200
@@ -7,6 +7,8 @@
"""
__docformat__ = "restructuredtext en"
+from logilab.common.decorators import iclassmethod
+
from cubicweb.appobject import AppObject
from cubicweb.view import NOINDEX, NOFOLLOW
from cubicweb.common import tags
@@ -205,3 +207,58 @@
class Form(FormMixIn, AppObject):
__metaclass__ = metafieldsform
__registry__ = 'forms'
+
+ parent_form = None
+
+ @property
+ def root_form(self):
+ """return the root form"""
+ if self.parent_form is None:
+ return self
+ return self.parent_form.root_form
+
+ @iclassmethod
+ def _fieldsattr(cls_or_self):
+ if isinstance(cls_or_self, type):
+ fields = cls_or_self._fields_
+ else:
+ fields = cls_or_self.fields
+ return fields
+
+ @iclassmethod
+ def field_by_name(cls_or_self, name, role='subject'):
+ """return field with the given name and role.
+ Raise FieldNotFound if the field can't be found.
+ """
+ for field in cls_or_self._fieldsattr():
+ if field.name == name and field.role == role:
+ return field
+ raise FieldNotFound(name)
+
+ @iclassmethod
+ def fields_by_name(cls_or_self, name, role='subject'):
+ """return a list of fields with the given name and role"""
+ return [field for field in cls_or_self._fieldsattr()
+ if field.name == name and field.role == role]
+
+ @iclassmethod
+ def remove_field(cls_or_self, field):
+ """remove a field from form class or instance"""
+ cls_or_self._fieldsattr().remove(field)
+
+ @iclassmethod
+ def append_field(cls_or_self, field):
+ """append a field to form class or instance"""
+ cls_or_self._fieldsattr().append(field)
+
+ @iclassmethod
+ def insert_field_before(cls_or_self, new_field, name, role='subject'):
+ field = cls_or_self.field_by_name(name, role)
+ fields = cls_or_self._fieldsattr()
+ fields.insert(fields.index(field), new_field)
+
+ @iclassmethod
+ def insert_field_after(cls_or_self, new_field, name, role='subject'):
+ field = cls_or_self.field_by_name(name, role)
+ fields = cls_or_self._fieldsattr()
+ fields.insert(fields.index(field)+1, new_field)
--- a/web/formfields.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/formfields.py Tue Sep 29 12:45:23 2009 +0200
@@ -209,7 +209,7 @@
return vocab
def form_init(self, form):
- """method called before by form_build_context to trigger potential field
+ """method called before by build_context to trigger potential field
initialization requiring the form instance
"""
pass
--- a/web/test/unittest_form.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/test/unittest_form.py Tue Sep 29 12:45:23 2009 +0200
@@ -82,7 +82,7 @@
form = EntityFieldsForm(self.request(login=u'toto'), None, entity=e)
field = StringField(name='login', eidparam=True)
form.append_field(field)
- form.form_build_context({})
+ form.build_context({})
self.assertEquals(form.form_field_display_value(field, {}), 'toto')
@@ -137,7 +137,7 @@
# fields tests ############################################################
def _render_entity_field(self, name, form):
- form.form_build_context({})
+ form.build_context({})
renderer = FormRenderer(self.req)
return form.field_by_name(name).render(form, renderer)
@@ -171,7 +171,7 @@
class FFForm(EntityFieldsForm):
data = FileField(format_field=StringField(name='data_format', max_length=50),
encoding_field=StringField(name='data_encoding', max_length=20))
- file = self.add_entity('File', name=u"pouet.txt", data_encoding=u'UTF-8',
+ file = self.add_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
data=Binary('new widgets system'))
form = FFForm(self.req, redirect_path='perdu.com', entity=file)
self.assertTextEquals(self._render_entity_field('data', form),
@@ -195,7 +195,7 @@
return 'ascii'
def form_field_format(self, field):
return 'text/plain'
- file = self.add_entity('File', name=u"pouet.txt", data_encoding=u'UTF-8',
+ file = self.add_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
data=Binary('new widgets system'))
form = EFFForm(self.req, redirect_path='perdu.com', entity=file)
self.assertTextEquals(self._render_entity_field('data', form),
--- a/web/views/autoform.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/autoform.py Tue Sep 29 12:45:23 2009 +0200
@@ -8,7 +8,7 @@
__docformat__ = "restructuredtext en"
_ = unicode
-from logilab.common.decorators import iclassmethod
+from logilab.common.decorators import iclassmethod, cached
from cubicweb import typed_eid
from cubicweb.web import stdmsgs, uicfg
@@ -194,6 +194,11 @@
"""true if the form needs enctype=multipart/form-data"""
return self._subform_needs_multipart()
+ def build_context(self, rendervalues=None):
+ super(AutomaticEntityForm, self).build_context(rendervalues)
+ for form in self.inlined_forms():
+ form.build_context(rendervalues)
+
def _subform_needs_multipart(self, _tested=None):
if _tested is None:
_tested = set()
@@ -337,6 +342,46 @@
# inlined forms support ####################################################
+ @cached
+ def inlined_form_views(self):
+ """compute and return list of inlined form views (hosting the inlined form object)
+ """
+ formviews = []
+ entity = self.edited_entity
+ for rschema, ttypes, role in self.inlined_relations():
+ # show inline forms only if there's one possible target type
+ # for rschema
+ if len(ttypes) != 1:
+ self.warning('entity related by the %s relation should have '
+ 'inlined form but there is multiple target types, '
+ 'dunno what to do', rschema)
+ continue
+ ttype = ttypes[0].type
+ if self.should_inline_relation_form(rschema, ttype, role):
+ formviews += self.inline_edition_form_view(rschema, ttype, role)
+ if role == 'subject':
+ card = rschema.rproperty(entity.e_schema, ttype, 'cardinality')[0]
+ else:
+ card = rschema.rproperty(ttype, entity.e_schema, 'cardinality')[1]
+ # there is no related entity and we need at least one: we need to
+ # display one explicit inline-creation view
+ if self.should_display_inline_creation_form(rschema, formviews, card):
+ formviews += self.inline_creation_form_view(rschema, ttype, role)
+ # we can create more than one related entity, we thus display a link
+ # to add new related entities
+ if self.should_display_add_new_relation_link(rschema, formviews, card):
+ addnewlink = self.vreg['views'].select(
+ 'inline-addnew-link', self.req,
+ etype=ttype, rtype=rschema, role=role,
+ peid=self.edited_entity.eid, pform=self, card=card)
+ formviews.append(addnewlink)
+ return formviews
+
+ def inlined_forms(self):
+ for formview in self.inlined_form_views():
+ if formview.form: # may be None for the addnew_link artefact form
+ yield formview.form
+
def should_inline_relation_form(self, rschema, targettype, role):
"""return true if the given relation with entity has role and a
targettype target should be inlined
@@ -344,25 +389,6 @@
return self.rinlined.etype_get(self.edited_entity.id, rschema, role,
targettype)
- def display_inline_edition_form(self, w, rschema, targettype, role,
- i18nctx):
- """display inline forms for already related entities.
-
- Return True if some inlined form are actually displayed
- """
- existant = False
- entity = self.edited_entity
- related = entity.has_eid() and entity.related(rschema, role)
- if related:
- # display inline-edition view for all existing related entities
- for i, relentity in enumerate(related.entities()):
- if relentity.has_perm('update'):
- w(self.view('inline-edition', related, row=i, col=0,
- rtype=rschema, role=role, ptype=entity.e_schema,
- peid=entity.eid, i18nctx=i18nctx))
- existant = True
- return existant
-
def should_display_inline_creation_form(self, rschema, existant, card):
"""return true if a creation form should be inlined
@@ -370,17 +396,6 @@
"""
return not existant and card in '1+' or self.req.form.has_key('force_%s_display' % rschema)
- def display_inline_creation_form(self, w, rschema, targettype, role,
- i18nctx):
- """display inline forms to a newly related (hence created) entity.
-
- Return True if some inlined form are actually displayed
- """
- entity = self.edited_entity
- w(self.view('inline-creation', None, etype=targettype,
- peid=entity.eid, ptype=entity.e_schema,
- rtype=rschema, role=role, i18nctx=i18nctx))
-
def should_display_add_new_relation_link(self, rschema, existant, card):
"""return true if we should add a link to add a new creation form
(through ajax call)
@@ -398,6 +413,29 @@
"""
return card in '1?'
+ def inline_edition_form_view(self, rschema, ttype, role):
+ """yield inline form views for already related entities through the
+ given relation
+ """
+ entity = self.edited_entity
+ related = entity.has_eid() and entity.related(rschema, role)
+ if related:
+ vvreg = self.vreg['views']
+ # display inline-edition view for all existing related entities
+ for i, relentity in enumerate(related.entities()):
+ if relentity.has_perm('update'):
+ yield vvreg.select('inline-edition', self.req, related,
+ row=i, col=0, rtype=rschema, role=role,
+ peid=entity.eid, pform=self)
+
+ def inline_creation_form_view(self, rschema, ttype, role):
+ """yield inline form views to a newly related (hence created) entity
+ through the given relation
+ """
+ yield self.vreg['views'].select('inline-creation', self.req,
+ etype=ttype, rtype=rschema, role=role,
+ peid=self.edited_entity.eid, pform=self)
+
def etype_relation_field(etype, rtype, role='subject'):
eschema = AutomaticEntityForm.schema.eschema(etype)
--- a/web/views/basecontrollers.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/basecontrollers.py Tue Sep 29 12:45:23 2009 +0200
@@ -354,7 +354,7 @@
entity.eid = varname
entity['pkey'] = propkey
form = self.vreg['forms'].select('edition', self.req, entity=entity)
- form.form_build_context()
+ form.build_context()
vfield = form.field_by_name('value')
renderer = FormRenderer(self.req)
return vfield.render(form, renderer, tabindex=tabindex) \
@@ -381,8 +381,7 @@
view = self.vreg['views'].select('inline-creation', self.req,
etype=ttype, peid=peid, rtype=rtype,
role=role)
- return self._call_view(view, etype=ttype, peid=peid,
- rtype=rtype, role=role, i18nctx=i18nctx)
+ return self._call_view(view, i18nctx=i18nctx)
@jsonize
def js_validate_form(self, action, names, values):
--- a/web/views/boxes.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/boxes.py Tue Sep 29 12:45:23 2009 +0200
@@ -60,8 +60,8 @@
('addrelated', None)):
for action in actions.get(category, ()):
if category == 'addrelated':
- warn('"addrelated" category is deprecated, use "moreaction"'
- ' category w/ "addrelated" submenu',
+ warn('[3.5] "addrelated" category is deprecated, use '
+ '"moreactions" category w/ "addrelated" submenu',
DeprecationWarning)
defaultmenu = self._get_menu('addrelated', _('add'), _('add'))
if action.submenu:
--- a/web/views/cwproperties.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/cwproperties.py Tue Sep 29 12:45:23 2009 +0200
@@ -216,7 +216,7 @@
eidparam=True))
subform.vreg = self.vreg
subform.form_add_hidden('pkey', key, eidparam=True)
- form.form_add_subform(subform)
+ form.add_subform(subform)
return subform
--- a/web/views/editforms.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/editforms.py Tue Sep 29 12:45:23 2009 +0200
@@ -14,6 +14,7 @@
from simplejson import dumps
from logilab.mtconverter import xml_escape
+from logilab.common.decorators import cached
from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
specified_etype_implements, yes)
@@ -65,7 +66,7 @@
done.add(entity.eid)
subform = self.vreg['forms'].select('base', self.req, entity=entity,
mainform=False)
- self.form_add_subform(subform)
+ self.add_subform(subform)
class DeleteConfFormView(FormViewMixIn, EntityView):
@@ -421,7 +422,7 @@
mainform=False)
# XXX rely on the EntityCompositeFormRenderer to put the eid input
form.remove_field(form.field_by_name('eid'))
- self.form_add_subform(form)
+ self.add_subform(form)
class TableEditFormView(FormViewMixIn, EntityView):
@@ -439,66 +440,83 @@
class InlineEntityEditionFormView(FormViewMixIn, EntityView):
+ """
+ :attr peid: the parent entity's eid hosting the inline form
+ :attr rtype: the relation bridging `etype` and `peid`
+ :attr role: the role played by the `peid` in the relation
+ :attr pform: the parent form where this inlined form is being displayed
+ """
id = 'inline-edition'
__select__ = non_final_entity() & match_kwargs('peid', 'rtype')
+
+ _select_attrs = ('peid', 'rtype', 'role', 'pform')
removejs = "removeInlinedEntity('%s', '%s', '%s')"
- def call(self, **kwargs):
- """redefine default call() method to avoid automatic
- insertions of <div class="section"> between each row of
- the resultset
- """
- rset = self.rset
- for i in xrange(len(rset)):
- self.wview(self.id, rset, row=i, **kwargs)
+ def __init__(self, *args, **kwargs):
+ for attr in self._select_attrs:
+ setattr(self, attr, kwargs.pop(attr, None))
+ super(InlineEntityEditionFormView, self).__init__(*args, **kwargs)
+
+ def _entity(self):
+ assert self.row is not None, self
+ return self.rset.get_entity(self.row, self.col)
- def cell_call(self, row, col, peid, rtype, role, i18nctx, **kwargs):
+ @property
+ @cached
+ def form(self):
+ entity = self._entity()
+ form = self.vreg['forms'].select('edition', self.req,
+ entity=entity,
+ form_renderer_id='inline',
+ mainform=False, copy_nav_params=False,
+ **self.extra_kwargs)
+ form.parent_form = self.pform
+ self.add_hiddens(form, entity)
+ return form
+
+ def cell_call(self, row, col, i18nctx, **kwargs):
"""
:param peid: the parent entity's eid hosting the inline form
:param rtype: the relation bridging `etype` and `peid`
:param role: the role played by the `peid` in the relation
"""
- entity = self.entity(row, col)
- divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (peid, rtype,
- entity.eid)
- self.render_form(entity, peid, rtype, role, i18nctx,
- divonclick=divonclick)
+ entity = self._entity()
+ divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (
+ self.peid, self.rtype, entity.eid)
+ self.render_form(i18nctx, divonclick=divonclick, **kwargs)
- def render_form(self, entity, peid, rtype, role, i18nctx, **kwargs):
+ def render_form(self, i18nctx, **kwargs):
"""fetch and render the form"""
- form = self.vreg['forms'].select('edition', self.req, entity=entity,
- form_renderer_id='inline',
- mainform=False, copy_nav_params=False)
- self.add_hiddens(form, entity, peid, rtype, role)
- divid = '%s-%s-%s' % (peid, rtype, entity.eid)
+ entity = self._entity()
+ divid = '%s-%s-%s' % (self.peid, self.rtype, entity.eid)
title = self.req.pgettext(i18nctx, 'This %s' % entity.e_schema)
- removejs = self.removejs % (peid, rtype, entity.eid)
- countkey = '%s_count' % rtype
+ removejs = self.removejs % (self.peid, self.rtype, entity.eid)
+ countkey = '%s_count' % self.rtype
try:
self.req.data[countkey] += 1
- except:
+ except KeyError:
self.req.data[countkey] = 1
- self.w(form.form_render(divid=divid, title=title, removejs=removejs,
- i18nctx=i18nctx,
- counter=self.req.data[countkey], **kwargs))
+ self.w(self.form.form_render(
+ divid=divid, title=title, removejs=removejs, i18nctx=i18nctx,
+ counter=self.req.data[countkey], **kwargs))
- def add_hiddens(self, form, entity, peid, rtype, role):
+ def add_hiddens(self, form, entity):
# to ease overriding (see cubes.vcsfile.views.forms for instance)
- if self.keep_entity(form, entity, peid, rtype):
+ if self.keep_entity(form, entity):
if entity.has_eid():
rval = entity.eid
else:
rval = INTERNAL_FIELD_VALUE
- form.form_add_hidden('edit%s-%s:%s' % (role[0], rtype, peid), rval)
- form.form_add_hidden(name='%s:%s' % (rtype, peid), value=entity.eid,
- id='rel-%s-%s-%s' % (peid, rtype, entity.eid))
+ form.form_add_hidden('edit%s-%s:%s' % (self.role[0], self.rtype, self.peid), rval)
+ form.form_add_hidden(name='%s:%s' % (self.rtype, self.peid), value=entity.eid,
+ id='rel-%s-%s-%s' % (self.peid, self.rtype, entity.eid))
- def keep_entity(self, form, entity, peid, rtype):
+ def keep_entity(self, form, entity):
if not entity.has_eid():
return True
# are we regenerating form because of a validation error ?
if form.form_previous_values:
- cdvalues = self.req.list_form_param(eid_param(rtype, peid),
+ cdvalues = self.req.list_form_param(eid_param(self.rtype, self.peid),
form.form_previous_values)
if unicode(entity.eid) not in cdvalues:
return False
@@ -506,24 +524,52 @@
class InlineEntityCreationFormView(InlineEntityEditionFormView):
+ """
+ :attr etype: the entity type being created in the inline form
+ """
id = 'inline-creation'
__select__ = (match_kwargs('peid', 'rtype')
& specified_etype_implements('Any'))
+ _select_attrs = InlineEntityEditionFormView._select_attrs + ('etype',)
removejs = "removeInlineForm('%s', '%s', '%s')"
- def call(self, etype, peid, rtype, role, i18nctx, **kwargs):
- """
- :param etype: the entity type being created in the inline form
- :param peid: the parent entity's eid hosting the inline form
- :param rtype: the relation bridging `etype` and `peid`
- :param role: the role played by the `peid` in the relation
- """
+ @cached
+ def _entity(self):
try:
- cls = self.vreg['etypes'].etype_class(etype)
+ cls = self.vreg['etypes'].etype_class(self.etype)
except:
self.w(self.req._('no such entity type %s') % etype)
return
self.initialize_varmaker()
entity = cls(self.req)
entity.eid = self.varmaker.next()
- self.render_form(entity, peid, rtype, role, i18nctx, **kwargs)
+ return entity
+
+ def call(self, i18nctx, **kwargs):
+ self.render_form(i18nctx, **kwargs)
+
+
+class InlineAddNewLinkView(InlineEntityCreationFormView):
+ """
+ :attr card: the cardinality of the relation according to role of `peid`
+ """
+ id = 'inline-addnew-link'
+ __select__ = (match_kwargs('peid', 'rtype')
+ & specified_etype_implements('Any'))
+
+ _select_attrs = InlineEntityCreationFormView._select_attrs + ('card',)
+ form = None # no actual form wrapped
+
+ def call(self, i18nctx, **kwargs):
+ divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role, self.peid)
+ self.w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
+ % divid)
+ js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % (
+ self.peid, self.etype, self.rtype, self.role, i18nctx)
+ if self.pform.should_hide_add_new_relation_link(self.rtype, self.card):
+ js = "toggleVisibility('%s'); %s" % (divid, js)
+ __ = self.req.pgettext
+ self.w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
+ % (self.rtype, self.peid, js, __(i18nctx, 'add a %s' % self.etype)))
+ self.w(u'</div>')
+ self.w(u'<div class="trame_grise"> </div>')
--- a/web/views/formrenderers.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/formrenderers.py Tue Sep 29 12:45:23 2009 +0200
@@ -171,7 +171,6 @@
or (field.name, field.role) in form.internal_fields)
def render_fields(self, w, form, values):
- form.form_build_context(values)
fields = self._render_hidden_fields(w, form)
if fields:
self._render_fields(fields, w, form)
@@ -312,7 +311,7 @@
_main_display_fields = None
def render_fields(self, w, form, values):
- if not form.is_subform:
+ if form.parent_form is None:
w(u'<table class="listing">')
subfields = [field for field in form.forms[0].fields
if self.display_field(form, field)
@@ -328,14 +327,14 @@
w(u'<th>%s</th>' % self.req._(field.label))
w(u'</tr>')
super(EntityCompositeFormRenderer, self).render_fields(w, form, values)
- if not form.is_subform:
+ if form.parent_form is None:
w(u'</table>')
if self._main_display_fields:
super(EntityCompositeFormRenderer, self)._render_fields(
self._main_display_fields, w, form)
def _render_fields(self, fields, w, form):
- if form.is_subform:
+ if form.parent_form is not None:
entity = form.edited_entity
values = form.form_previous_values
qeid = eid_param('eid', entity.eid)
@@ -478,51 +477,23 @@
def inline_entities_form(self, w, form):
"""create a form to edit entity's inlined relations"""
- if not hasattr(form, 'inlined_relations'):
+ if not hasattr(form, 'inlined_form_views'):
return
- for rschema, targettypes, role in form.inlined_relations():
- # show inline forms only if there's one possible target type
- # for rschema
- if len(targettypes) != 1:
- self.warning('entity related by the %s relation should have '
- 'inlined form but there is multiple target types, '
- 'dunno what to do', rschema)
- continue
- targettype = targettypes[0].type
- if form.should_inline_relation_form(rschema, targettype, role):
- self.inline_relation_form(w, form, rschema, targettype, role)
+ keysinorder = []
+ formviews = form.inlined_form_views()
+ for formview in formviews:
+ if not (formview.rtype, formview.role) in keysinorder:
+ keysinorder.append( (formview.rtype, formview.role) )
+ for key in keysinorder:
+ self.inline_relation_form(w, form, [fv for fv in formviews
+ if (fv.rtype, fv.role) == key])
- def inline_relation_form(self, w, form, rschema, targettype, role):
- entity = form.edited_entity
- __ = self.req.pgettext
- i18nctx = 'inlined:%s.%s.%s' % (entity.e_schema, rschema, role)
- w(u'<div id="inline%sslot">' % rschema)
- existant = form.display_inline_edition_form(w, rschema, targettype,
- role, i18nctx)
- if role == 'subject':
- card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
- else:
- card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
- # there is no related entity and we need at least one: we need to
- # display one explicit inline-creation view
- if form.should_display_inline_creation_form(rschema, existant, card):
- form.display_inline_creation_form(w, rschema, targettype,
- role, i18nctx)
- existant = True
- # we can create more than one related entity, we thus display a link
- # to add new related entities
- if form.should_display_add_new_relation_link(rschema, existant, card):
- divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
- w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
- % divid)
- js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % (
- entity.eid, targettype, rschema, role, i18nctx)
- if form.should_hide_add_new_relation_link(rschema, card):
- js = "toggleVisibility('%s'); %s" % (divid, js)
- w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
- % (rschema, entity.eid, js, __(i18nctx, 'add a %s' % targettype)))
- w(u'</div>')
- w(u'<div class="trame_grise"> </div>')
+ def inline_relation_form(self, w, form, formviews):
+ i18nctx = 'inlined:%s.%s.%s' % (form.edited_entity.e_schema,
+ formviews[0].rtype, formviews[0].role)
+ w(u'<div id="inline%sslot">' % formviews[0].rtype)
+ for formview in formviews:
+ w(formview.render(i18nctx=i18nctx, row=formview.row, col=formview.col))
w(u'</div>')
@@ -559,7 +530,6 @@
return '\n'.join(data)
def render_fields(self, w, form, values):
- form.form_build_context(values)
w(u'<fieldset id="fs-%(divid)s">' % values)
fields = self._render_hidden_fields(w, form)
w(u'</fieldset>')
--- a/web/views/forms.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/forms.py Tue Sep 29 12:45:23 2009 +0200
@@ -10,7 +10,6 @@
from warnings import warn
from logilab.common.compat import any
-from logilab.common.decorators import iclassmethod
from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
@@ -56,7 +55,6 @@
"""
id = 'base'
- is_subform = False
internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
# attributes overrideable by subclasses or through __init__
@@ -84,6 +82,8 @@
self.form_add_hidden(key, val)
elif hasattr(self.__class__, key) and not key[0] == '_':
setattr(self, key, val)
+ else:
+ self.extra_kwargs[key] = val
# skip other parameters, usually given for selection
# (else write a custom class to handle them)
if mainform:
@@ -101,52 +101,6 @@
if 'domid' in kwargs:# session key changed
self.restore_previous_post(self.session_key())
- @iclassmethod
- def _fieldsattr(cls_or_self):
- if isinstance(cls_or_self, type):
- fields = cls_or_self._fields_
- else:
- fields = cls_or_self.fields
- return fields
-
- @iclassmethod
- def field_by_name(cls_or_self, name, role='subject'):
- """return field with the given name and role.
- Raise FieldNotFound if the field can't be found.
- """
- for field in cls_or_self._fieldsattr():
- if field.name == name and field.role == role:
- return field
- raise form.FieldNotFound(name)
-
- @iclassmethod
- def fields_by_name(cls_or_self, name, role='subject'):
- """return a list of fields with the given name and role"""
- return [field for field in cls_or_self._fieldsattr()
- if field.name == name and field.role == role]
-
- @iclassmethod
- def remove_field(cls_or_self, field):
- """remove a field from form class or instance"""
- cls_or_self._fieldsattr().remove(field)
-
- @iclassmethod
- def append_field(cls_or_self, field):
- """append a field to form class or instance"""
- cls_or_self._fieldsattr().append(field)
-
- @iclassmethod
- def insert_field_before(cls_or_self, new_field, name, role='subject'):
- field = cls_or_self.field_by_name(name, role)
- fields = cls_or_self._fieldsattr()
- fields.insert(fields.index(field), new_field)
-
- @iclassmethod
- def insert_field_after(cls_or_self, new_field, name, role='subject'):
- field = cls_or_self.field_by_name(name, role)
- fields = cls_or_self._fieldsattr()
- fields.insert(fields.index(field)+1, new_field)
-
@property
def form_needs_multipart(self):
"""true if the form needs enctype=multipart/form-data"""
@@ -174,6 +128,7 @@
"""render this form, using the renderer given in args or the default
FormRenderer()
"""
+ self.build_context(values)
renderer = values.pop('renderer', None)
if renderer is None:
renderer = self.form_default_renderer()
@@ -184,7 +139,7 @@
self.req, rset=self.rset,
row=self.row, col=self.col)
- def form_build_context(self, rendervalues=None):
+ def build_context(self, rendervalues=None):
"""build form context values (the .context attribute which is a
dictionary with field instance as key associated to a dictionary
containing field 'name' (qualified), 'id', 'value' (for display, always
@@ -193,6 +148,8 @@
rendervalues is an optional dictionary containing extra kwargs given to
form_render()
"""
+ if self.context is not None:
+ return # already built
self.context = context = {}
# ensure rendervalues is a dict
if rendervalues is None:
@@ -224,6 +181,8 @@
if value is None:
if field.name in rendervalues:
value = rendervalues[field.name]
+ elif field.name in self.extra_kwargs:
+ value = self.extra_kwargs[field.name]
else:
value = self.form_field_value(field, load_bytes)
if callable(value):
@@ -346,9 +305,9 @@
searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role)
for eid in value]
# remove associated __linkto hidden fields
- for field in self.fields_by_name('__linkto'):
+ for field in self.root_form.fields_by_name('__linkto'):
if field.initial in searchedvalues:
- self.remove_field(field)
+ self.root_form.remove_field(field)
else:
value = None
return value
@@ -372,7 +331,7 @@
self.form_renderer_id, self.req, rset=self.rset, row=self.row,
col=self.col, entity=self.edited_entity)
- def form_build_context(self, values=None):
+ def build_context(self, values=None):
"""overriden to add edit[s|o] hidden fields and to ensure schema fields
have eidparam set to True
@@ -380,6 +339,8 @@
associated field before the (potential) modification made when
submitting the form.
"""
+ if self.context is not None:
+ return
eschema = self.edited_entity.e_schema
for field in self.fields[:]:
for field in field.actual_fields(self):
@@ -389,7 +350,7 @@
eschema.has_object_relation(fieldname))):
field.eidparam = True
self.fields.append(HiddenInitialValueField(field))
- return super(EntityFieldsForm, self).form_build_context(values)
+ return super(EntityFieldsForm, self).build_context(values)
def form_field_value(self, field, load_bytes=False):
"""return field's *typed* value
@@ -539,31 +500,28 @@
return False
-class CompositeForm(FieldsForm):
+class CompositeFormMixIn(object):
"""form composed of sub-forms"""
id = 'composite'
form_renderer_id = id
def __init__(self, *args, **kwargs):
- super(CompositeForm, self).__init__(*args, **kwargs)
+ super(CompositeFormMixIn, self).__init__(*args, **kwargs)
self.forms = []
- def form_add_subform(self, subform):
+ def add_subform(self, subform):
"""mark given form as a subform and append it"""
- subform.is_subform = True
+ subform.parent_form = self
self.forms.append(subform)
+ def build_context(self, rendervalues=None):
+ super(CompositeFormMixIn, self).build_context(rendervalues)
+ for form in self.forms:
+ form.build_context(rendervalues)
+
-class CompositeEntityForm(EntityFieldsForm):
- """form composed of sub-forms"""
- id = 'composite'
- form_renderer_id = id
+class CompositeForm(CompositeFormMixIn, FieldsForm):
+ pass
- def __init__(self, *args, **kwargs):
- super(CompositeEntityForm, self).__init__(*args, **kwargs)
- self.forms = []
-
- def form_add_subform(self, subform):
- """mark given form as a subform and append it"""
- subform.is_subform = True
- self.forms.append(subform)
+class CompositeEntityForm(CompositeFormMixIn, EntityFieldsForm):
+ pass # XXX why is this class necessary?
--- a/web/views/workflow.py Tue Sep 29 10:39:02 2009 +0200
+++ b/web/views/workflow.py Tue Sep 29 12:45:23 2009 +0200
@@ -84,7 +84,7 @@
subform = self.vreg['forms'].select('edition', self.req, entity=trinfo,
mainform=False)
subform.field_by_name('by_transition').widget = fwdgs.HiddenInput()
- form.form_add_subform(subform)
+ form.add_subform(subform)
self.w(form.form_render(wf_info_for=entity.eid,
by_transition=transition.eid))