# HG changeset patch # User Sylvain Thénault # Date 1254221090 -7200 # Node ID 11ce4682187da7bb61246f8ca7f712cecc901a1b # Parent 8832e231fad776cad36351d3c8ec3607dcc90a7a [form] important refactoring of inlined forms to get proper separation of form object creation / rendering diff -r 8832e231fad7 -r 11ce4682187d web/views/autoform.py --- a/web/views/autoform.py Tue Sep 29 12:44:06 2009 +0200 +++ b/web/views/autoform.py Tue Sep 29 12:44:50 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) diff -r 8832e231fad7 -r 11ce4682187d web/views/basecontrollers.py --- a/web/views/basecontrollers.py Tue Sep 29 12:44:06 2009 +0200 +++ b/web/views/basecontrollers.py Tue Sep 29 12:44:50 2009 +0200 @@ -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): diff -r 8832e231fad7 -r 11ce4682187d web/views/editforms.py --- a/web/views/editforms.py Tue Sep 29 12:44:06 2009 +0200 +++ b/web/views/editforms.py Tue Sep 29 12:44:50 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) @@ -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
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'
' + % 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'+ %s.' + % (self.rtype, self.peid, js, __(i18nctx, 'add a %s' % self.etype))) + self.w(u'
') + self.w(u'
 
') diff -r 8832e231fad7 -r 11ce4682187d web/views/formrenderers.py --- a/web/views/formrenderers.py Tue Sep 29 12:44:06 2009 +0200 +++ b/web/views/formrenderers.py Tue Sep 29 12:44:50 2009 +0200 @@ -474,51 +474,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'
' % 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'
' - % 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'+ %s.' - % (rschema, entity.eid, js, __(i18nctx, 'add a %s' % targettype))) - w(u'
') - w(u'
 
') + 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'
' % formviews[0].rtype) + for formview in formviews: + w(formview.render(i18nctx=i18nctx, row=formview.row, col=formview.col)) w(u'
')