API cleanup (closes #2728920)

  • drop old bw compat
  • split form/renderer builder and reledit layout API
  • simplify & cleanup entry points (.apply vs .__call__)
authorAurelien Campeas <aurelien.campeas@logilab.fr>
changesetb0148b121674
branchdefault
phasedraft
hiddenno
parent revision#fab518d29925 [merge] backport stable into default
child revision<not specified>
files modified by this revision
views/reledit.py
# HG changeset patch
# User Aurelien Campeas <aurelien.campeas@logilab.fr>
# Date 1362565307 -3600
# Wed Mar 06 11:21:47 2013 +0100
# Node ID b0148b121674521499f355f3b52d3f6751a1d0ca
# Parent fab518d299258f2f185e3a21f9e7ce22e65419a2
API cleanup (closes #2728920)

* drop old bw compat

* split form/renderer builder and reledit layout API

* simplify & cleanup entry points (.apply vs .__call__)

diff --git a/views/reledit.py b/views/reledit.py
@@ -23,11 +23,10 @@
1  from warnings import warn
2 
3  import cwtags.tag as t
4 
5  from logilab.mtconverter import xml_escape
6 -from logilab.common.deprecation import deprecated, class_renamed
7  from logilab.common.decorators import cached
8  from logilab.common.registry import yes
9 
10  from cubicweb import neg_role, appobject
11  from cubicweb.schema import display_name
@@ -57,15 +56,10 @@
12 
13  def build_divid(rtype, role, entity_eid):
14      """ builds an id for the root div of a reledit widget """
15      return '%s-%s-%s' % (rtype, role, entity_eid)
16 
17 -@cached
18 -def compute_ttypes(rschema, role):
19 -    warn('[inlinedit 0.3.0] compute_ttypes is deprecated, '
20 -         'use rschema.targets(etype=, role=)', DeprecationWarning)
21 -    return rschema.targets(role=role)
22 
23  class RelatedEntityEdit(EntityView):
24      __regid__ = 'edit-related-entity'
25      __select__ = EntityView.__select__ & match_kwargs('action', 'reload')
26      # back path to container entity thru rtype/role
@@ -92,14 +86,14 @@
27 
28      def cell_call(self, row, col, action=None, reload=False, extradata=None, evid='incontext', **kwargs):
29          entity = self.cw_rset.get_entity(row, col)
30          divid = 'none-none-%s' % entity.eid
31          value = self._cw.view(evid, entity.as_rset())
32 -        form_handler = self._cw.vreg['reledit'].select('entity-form-handler', self._cw, entity=entity)
33 -        form_handler.apply(self.w, value, entity, divid, reload, action,
34 -                           self._may_edit(entity), self._may_delete(entity),
35 -                           extradata=extradata)
36 +        form_handler = self._cw.vreg['inlinedit'].select('entity-form-handler', self._cw)
37 +        form_handler(self.w, value, entity, divid, reload, action,
38 +                     self._may_edit(entity), self._may_delete(entity),
39 +                     extradata=extradata)
40 
41      def _may_edit(self, entity):
42          return entity.cw_has_perm('update')
43 
44      def _may_delete(self, entity):
@@ -141,13 +135,12 @@
45              value = self.entity.printable_value(rschema.type)
46          if not self._should_edit_attribute(rschema):
47              self.w(value)
48              return
49          value = value or self._compute_default_value(rschema, role)
50 -        form_handler = self._cw.vreg['reledit'].select('relation-form-handler', self._cw,
51 -                                                       entity=self.entity, rtype=rschema.type)
52 -        form_handler.apply(self.w, value, self.entity, rschema, role, divid, reload, action)
53 +        form_handler = self._cw.vreg['inlinedit'].select('relation-form-handler', self._cw)
54 +        form_handler(self.w, value, self.entity, rschema, role, divid, reload, action)
55 
56      def _compute_value_editperm(self, rschema, role, rvid, **viewargs):
57          related_rset = self.entity.related(rschema.type, role)
58          if related_rset:
59              value = self._cw.view(rvid, related_rset, role=role, **viewargs)
@@ -158,26 +151,23 @@
60      def _handle_relation(self, rschema, role, divid, reload, action):
61          rvid = self._rules.get('rvid', 'autolimited')
62          value, permission = self._compute_value_editperm(rschema, role, rvid)
63          if not permission:
64              return self.w(value)
65 -        form_handler = self._cw.vreg['reledit'].select('relation-form-handler', self._cw,
66 -                                                       entity=self.entity, rtype=rschema.type)
67 -        form_handler.apply(self.w, value, self.entity, rschema, role, divid,
68 -                           reload, action)
69 +        form_handler = self._cw.vreg['inlinedit'].select('relation-form-handler', self._cw)
70 +        form_handler(self.w, value, self.entity, rschema, role, divid, reload, action)
71 
72      def _handle_composite(self, rschema, role, divid, reload, action):
73          rvid = self._rules.get('rvid', 'edit-related-entity')
74          value, permission = self._compute_value_editperm(rschema, role, rvid,
75                                                           reload=reload, action=action,
76                                                           container=self.entity.eid,
77                                                           topleveldiv=divid,
78                                                           rtype=rschema.type)
79 -        form_handler = self._cw.vreg['reledit'].select('relation-form-handler', self._cw,
80 -                                                       entity=self.entity, rtype=rschema.type)
81 -        form_handler.apply(self.w, value, self.entity, rschema, role, divid,
82 -                           reload, action, self._may_add_related(rschema, role), False)
83 +        form_handler = self._cw.vreg['inlinedit'].select('relation-form-handler', self._cw)
84 +        form_handler(self.w, value, self.entity, rschema, role, divid,
85 +                     reload, action, self._may_add_related(rschema, role), False)
86 
87      def _compute_reload(self, rschema, role, reload):
88          ctrl_reload = self._rules.get('reload', reload)
89          if callable(ctrl_reload):
90              ctrl_reload = ctrl_reload(self.entity)
@@ -223,43 +213,21 @@
91          if not entity.cw_has_perm('update'):
92              return False
93          rdef = entity.e_schema.rdef(rschema)
94          return rdef.has_perm(self._cw, 'update', eid=entity.eid)
95 
96 -    should_edit_attributes = deprecated('[3.9] should_edit_attributes is deprecated,'
97 -                                        ' use _should_edit_attribute instead',
98 -                                        _should_edit_attribute)
99 -
100      def _should_edit_relation(self, rschema, role):
101          eeid = self.entity.eid
102          perm_args = {'fromeid': eeid} if role == 'subject' else {'toeid': eeid}
103          return rschema.has_perm(self._cw, 'add', **perm_args)
104 
105 -    should_edit_relations = deprecated('[3.9] should_edit_relations is deprecated,'
106 -                                       ' use _should_edit_relation instead',
107 -                                       _should_edit_relation)
108 
109 -
110 -class ReleditFormHandler(appobject.AppObject):
111 -    __registry__ = 'reledit'
112 -    __abstract__ = True
113 -
114 +class FormBuilderMixin(object):
115      _form_renderer_id = 'base'
116 
117 -    _onclick = (u"cw.inlinedit.loadInlineForm(%s);")
118 -    _cancelclick = "cw.inlinedit.cleanupAfterCancel('%s', '%s')"
119 -
120 -    # for both edit_rtype and edit_related
121 -    _editzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
122 -    _editzonemsg = _('click to edit this field')
123 -    _editzonelogo = 'pen_icon.png'
124 -
125 -    def _build_edit_zone(self):
126 -        return self._editzone % {'msg' : xml_escape(_(self._cw._(self._editzonemsg))),
127 -                                 'logo': xml_escape(self._cw.data_url(self._editzonelogo))}
128 -
129      def _setup_form_arguments(self, form, event_args):
130 +        """ add hidden fields for each passed arguments """
131          for pname, pvalue in event_args.iteritems():
132              if pname == 'reload':
133                  pvalue = json_dumps(pvalue)
134              form.add_hidden('__reledit|' + pname, pvalue)
135 
@@ -274,152 +242,18 @@
136                          button = copy.deepcopy(button)
137                          button.cwaction = None
138                          button.onclick = cancelclick
139                      form_buttons.append(button)
140              form.form_buttons = form_buttons
141 -        else: # base
142 +        else: # base, let's stick a validate/cancel pair
143              form.form_buttons = [SubmitButton(),
144                                   Button(stdmsgs.BUTTON_CANCEL, onclick=cancelclick)]
145 
146 -    def wrap_form(self, divid, value, form, renderer,
147 -                  _edit_related, add_related, _delete_related,
148 -                  view_form_method):
149 -        w = self.w
150 -        with t.div(w, id='%s-reledit' % divid,
151 -                   onmouseout="jQuery('#%s').addClass('hidden')" % divid,
152 -                   onmouseover="jQuery('#%s').removeClass('hidden')" % divid,
153 -                   Class='releditField'):
154 -            with t.div(w, id='%s-value' % divid, Class='editableFieldValue'):
155 -                w(value)
156 -            form.render(renderer=renderer, w=w)
157 -            with t.div(w, id=divid, Class='editableField hidden'):
158 -                view_form_method()
159 -
160 -class ReleditEntityFormHandler(ReleditFormHandler):
161 -    __regid__ = 'entity-form-handler'
162 +class RelationFormBuilder(FormBuilderMixin, appobject.AppObject):
163 +    __registry__ = 'inlinedit'
164 +    __regid__ = 'relation-form-builder'
165      __select__ = yes()
166 -    _form_renderer_id = 'base'
167 -    # ui side actions/buttons
168 -    _deletezone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
169 -    _deletemsg = _('click to delete this value')
170 -    _deletezonelogo = 'cancel.png'
171 -
172 -    _action_formid = {'edit-related': 'edition',
173 -                      'delete-related': 'deleteconf'}
174 -
175 -    def apply(self, w, value, entity, divid, reload, action, edit_related=False,
176 -              delete_related=False, extradata=None, **formargs):
177 -        self.w = w
178 -        form, renderer = self._build_form(entity, divid, reload, action,
179 -                                          extradata=extradata, **formargs)
180 -        self.view_form(divid, value, form, renderer, edit_related, delete_related)
181 -
182 -    def _build_delete_zone(self):
183 -        return self._deletezone % {'msg': xml_escape(self._cw._(self._deletemsg)),
184 -                                   'logo': xml_escape(self._cw.data_url(self._deletezonelogo))}
185 -
186 -    def _build_args(self, entity, reload, action, extradata=None):
187 -        divid = 'none-none-%s' % (entity.eid)
188 -        event_args = {'divid' : divid, 'eid' : entity.eid, 'reload' : reload,
189 -                      'action': action, 'fname' : u'edit_related_form'}
190 -        if extradata:
191 -            event_args.update(extradata)
192 -        return event_args
193 -
194 -    def _build_renderer(self, entity):
195 -        return self._cw.vreg['formrenderers'].select(
196 -            self._form_renderer_id, self._cw, entity=entity,
197 -            display_label=True, table_class='attributeForm',
198 -            display_help=False, button_bar_class='buttonbar',
199 -            display_progress_div=False)
200 -
201 -    def _build_form(self, entity, divid, reload, action, extradata=None, **formargs):
202 -        event_args = self._build_args(entity, reload, action, extradata)
203 -        if not action or action == 'add-related':
204 -            # the later is handled by the relation form
205 -            form = _DummyForm()
206 -            form.event_args = event_args
207 -            return form, None
208 -        formid = self._action_formid.get(action, 'base')
209 -        form = self._cw.vreg['forms'].select(
210 -            formid, self._cw, rset=entity.as_rset(), entity=entity,
211 -            domid='%s-form' % divid, formtype='inlined',
212 -            action=self._cw.build_url('validateform', __onsuccess='window.parent.cw.inlinedit.onSuccess'),
213 -            cwtarget='eformframe', cssclass='releditForm',
214 -            **formargs)
215 -        # pass reledit arguments
216 -        self._setup_form_arguments(form, event_args)
217 -        # handle buttons
218 -        self._setup_form_buttons(form, divid)
219 -        form.event_args = event_args
220 -        return form, self._build_renderer(entity)
221 -
222 -    def wrap_form(self, divid, value, form, renderer,
223 -                  _edit_related, _add_related, _delete_related,
224 -                  view_form_method):
225 -        w = self.w
226 -        with t.div(w, id='%s-reledit' % divid,
227 -                   onmouseout="jQuery('#%s').addClass('hidden')" % divid,
228 -                   onmouseover="jQuery('#%s').removeClass('hidden')" % divid,
229 -                   Class='releditField'):
230 -            with t.div(w, id='%s-value' % divid,
231 -                       Class='editableFieldValue'):
232 -                w(value)
233 -            form.render(w=w, renderer=renderer)
234 -            with t.div(w, id=divid, Class='editableField hidden'):
235 -                view_form_method()
236 -
237 -    def _edit_action(self, divid, args):
238 -        w = self.w
239 -        args['action'] = 'edit-related'
240 -        with t.div(w, id='%s-update' % divid,
241 -                   onclick=xml_escape(self._onclick % json_dumps(args)),
242 -                   title=self._cw._(self._editzonemsg),
243 -                   Class='editableField'):
244 -            w(self._build_edit_zone())
245 -
246 -    def _del_action(self, divid, args):
247 -        w = self.w
248 -        args['action'] = 'delete-related'
249 -        with t.div(w, id='%s-delete' % divid,
250 -                   onclick=xml_escape(self._onclick % json_dumps(args)),
251 -                   title=self._cw._(self._deletemsg),
252 -                   Class="editableField"):
253 -            w(self._build_delete_zone())
254 -
255 -    def view_form(self, divid, value, form=None, renderer=None, edit_related=False, delete_related=False):
256 -        def _view_form():
257 -            args = form.event_args.copy()
258 -            if edit_related:
259 -                self._edit_action(divid, args)
260 -            if delete_related:
261 -                self._del_action(divid, args)
262 -        self.wrap_form(divid, value, form, renderer, edit_related, delete_related, False, _view_form)
263 -
264 -class ReleditRelationFormHandler(ReleditFormHandler):
265 -    __select__ = yes()
266 -    __registry__ = 'reledit'
267 -    __regid__ = 'relation-form-handler'
268 -
269 -    # ui side actions/buttons
270 -    _addzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
271 -    _addmsg = _('click to add a value')
272 -    _addzonelogo = 'plus.png'
273 -
274 -    _action_formid = {'edit-rtype': 'base',
275 -                      'add-related': 'edition'}
276 -
277 -    def apply(self, w, value, entity, rschema, role, divid, reload, action,
278 -              add_related=False, edit_rtype=True, extradata=None, **formargs):
279 -        self.w = w
280 -        form, renderer = self._build_form(entity, rschema, role, divid, reload, action,
281 -                                          extradata=None, **formargs)
282 -        self.view_form(divid, value, form, renderer, add_related, edit_rtype)
283 -
284 -    def _build_add_zone(self):
285 -        return self._addzone % {'msg': xml_escape(self._cw._(self._addmsg)),
286 -                                'logo': xml_escape(self._cw.data_url(self._addzonelogo))}
287 
288      def _build_args(self, entity, rtype, role, reload, action, extradata=None):
289          divid = build_divid(rtype, role, entity.eid)
290          event_args = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype,
291                        'reload' : reload, 'action': action,
@@ -449,12 +283,12 @@
292              display_label=display_label,
293              table_class='attributeForm' if display_label else '',
294              display_help=False, button_bar_class='buttonbar',
295              display_progress_div=False)
296 
297 -    def _build_form(self, entity, rschema, role, divid, reload, action,
298 -                    extradata=None, **formargs):
299 +    def __call__(self, entity, rschema, role, divid, reload, action,
300 +                 extradata=None, **formargs):
301          rtype = rschema.type
302          event_args = self._build_args(entity, rtype, role, reload, action, extradata)
303          if not action:
304              form = _DummyForm()
305              form.event_args = event_args
@@ -476,37 +310,201 @@
306          if formid == 'base':
307              field = form.field_by_name(rtype, role, entity.e_schema)
308              form.append_field(field)
309          return form, self._build_renderer(edit_entity, label)
310 
311 -    def _add_action(self, divid, args):
312 -        w = self.w
313 +
314 +class EntityFormBuilder(FormBuilderMixin, appobject.AppObject):
315 +    __registry__ = 'inlinedit'
316 +    __regid__ = 'entity-form-builder'
317 +    __select__ = yes()
318 +
319 +    def _build_args(self, entity, reload, action, extradata=None):
320 +        divid = 'none-none-%s' % (entity.eid)
321 +        event_args = {'divid' : divid, 'eid' : entity.eid, 'reload' : reload,
322 +                      'action': action, 'fname' : u'edit_related_form'}
323 +        if extradata:
324 +            event_args.update(extradata)
325 +        return event_args
326 +
327 +    def _build_renderer(self, entity):
328 +        return self._cw.vreg['formrenderers'].select(
329 +            self._form_renderer_id, self._cw, entity=entity,
330 +            display_label=True, table_class='attributeForm',
331 +            display_help=False, button_bar_class='buttonbar',
332 +            display_progress_div=False)
333 +
334 +    def __call__(self, entity, divid, reload, action, extradata=None, **formargs):
335 +        event_args = self._build_args(entity, reload, action, extradata)
336 +        if not action or action == 'add-related':
337 +            # the later is handled by the relation form
338 +            form = _DummyForm()
339 +            form.event_args = event_args
340 +            return form, None
341 +        formid = self._action_formid.get(action, 'base')
342 +        form = self._cw.vreg['forms'].select(
343 +            formid, self._cw, rset=entity.as_rset(), entity=entity,
344 +            domid='%s-form' % divid, formtype='inlined',
345 +            action=self._cw.build_url('validateform', __onsuccess='window.parent.cw.inlinedit.onSuccess'),
346 +            cwtarget='eformframe', cssclass='releditForm',
347 +            **formargs)
348 +        # pass reledit arguments
349 +        self._setup_form_arguments(form, event_args)
350 +        # handle buttons
351 +        self._setup_form_buttons(form, divid)
352 +        form.event_args = event_args
353 +        return form, self._build_renderer(entity)
354 +
355 +
356 +class InlinedFormMixin(object):
357 +    _onclick = (u"cw.inlinedit.loadInlineForm(%s);")
358 +    _cancelclick = "cw.inlinedit.cleanupAfterCancel('%s', '%s')"
359 +
360 +    # for both edit_rtype and edit_related
361 +    _editzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
362 +    _editzonemsg = _('click to edit this field')
363 +    _editzonelogo = 'pen_icon.png'
364 +
365 +    def _build_edit_zone(self):
366 +        return self._editzone % {'msg' : xml_escape(_(self._cw._(self._editzonemsg))),
367 +                                 'logo': xml_escape(self._cw.data_url(self._editzonelogo))}
368 +
369 +
370 +
371 +class InlinedEntityFormHandler(InlinedFormMixin, appobject.AppObject):
372 +    __regid__ = 'entity-form-handler'
373 +    __registry__ = 'inlinedit'
374 +    __select__ = yes()
375 +
376 +    # ui side actions/buttons
377 +    _deletezone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
378 +    _deletemsg = _('click to delete this value')
379 +    _deletezonelogo = 'cancel.png'
380 +
381 +    _action_formid = {'edit-related': 'edition',
382 +                      'delete-related': 'deleteconf'}
383 +
384 +    def __call__(self, w, value, entity, divid, reload, action, edit_related=False,
385 +              delete_related=False, extradata=None, **formargs):
386 +        builder = self._cw.vreg['inlinedit'].select('entity-form-builder', self._cw)
387 +        form, renderer =  builder(entity, divid, reload, action, extradata=extradata, **formargs)
388 +        self.view_entity_form(w, divid, value, form, renderer, edit_related, delete_related)
389 +
390 +
391 +    def _build_delete_zone(self):
392 +        return self._deletezone % {'msg': xml_escape(self._cw._(self._deletemsg)),
393 +                                   'logo': xml_escape(self._cw.data_url(self._deletezonelogo))}
394 +
395 +    def _edit_action(self, w, divid, args):
396 +        args['action'] = 'edit-related'
397 +        with t.div(w, id='%s-update' % divid,
398 +                   onclick=xml_escape(self._onclick % json_dumps(args)),
399 +                   title=self._cw._(self._editzonemsg),
400 +                   Class='editableField'):
401 +            w(self._build_edit_zone())
402 +
403 +    def _del_action(self, w, divid, args):
404 +        args['action'] = 'delete-related'
405 +        with t.div(w, id='%s-delete' % divid,
406 +                   onclick=xml_escape(self._onclick % json_dumps(args)),
407 +                   title=self._cw._(self._deletemsg),
408 +                   Class="editableField"):
409 +            w(self._build_delete_zone())
410 +
411 +    def view_entity_form(self, w, divid, value, form, renderer, edit_related, delete_related):
412 +        """
413 +        :divid: dom id of the surrounding reledit section
414 +        :value: display string of the entity
415 +        :form: entity edition form
416 +        :edit_related: bool saying if related entity edition is allowed
417 +        :delete_related: bool saying if related entity deletion is allowed
418 +        :add_or_dele_form: callback building the add/delete ui actions
419 +
420 +        The last three elements allow subclassers to cheat a bit.
421 +        """
422 +        with t.div(w, id='%s-reledit' % divid,
423 +                   onmouseout="jQuery('#%s').addClass('hidden')" % divid,
424 +                   onmouseover="jQuery('#%s').removeClass('hidden')" % divid,
425 +                   Class='releditField'):
426 +            with t.div(w, id='%s-value' % divid,
427 +                       Class='editableFieldValue'):
428 +                w(value)
429 +            form.render(w=w, renderer=renderer)
430 +            if not edit_related and not delete_related:
431 +                return
432 +            with t.div(w, id=divid, Class='editableField hidden'):
433 +                args = form.event_args.copy()
434 +                if edit_related:
435 +                    self._edit_action(w, divid, args)
436 +                if delete_related:
437 +                    self._del_action(w, divid, args)
438 +
439 +
440 +class InlinedRelationFormHandler(InlinedFormMixin, appobject.AppObject):
441 +    __registry__ = 'inlinedit'
442 +    __regid__ = 'relation-form-handler'
443 +    __select__ = yes()
444 +
445 +    # ui side actions/buttons
446 +    _addzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
447 +    _addmsg = _('click to add a value')
448 +    _addzonelogo = 'plus.png'
449 +
450 +    _action_formid = {'edit-rtype': 'base',
451 +                      'add-related': 'edition'}
452 +
453 +    def __call__(self, w, value, entity, rschema, role, divid, reload, action,
454 +              add_related=False, extradata=None, **formargs):
455 +        """ if add_related, this is a composite relation, hence no relation edition """
456 +        formbuilder = self._cw.vreg['inlinedit'].select('relation-form-builder', self._cw)
457 +        form, renderer = formbuilder(entity, rschema, role, divid, reload, action,
458 +                                     extradata=None, **formargs)
459 +        self.view_relation_form(w, divid, value, form, renderer, add_related)
460 +
461 +
462 +    def _build_add_zone(self):
463 +        return self._addzone % {'msg': xml_escape(self._cw._(self._addmsg)),
464 +                                'logo': xml_escape(self._cw.data_url(self._addzonelogo))}
465 +
466 +    def _add_action(self, w, divid, args):
467          args['action'] = 'add-related'
468          with t.div(w, id='%s-add' % divid,
469                     onclick=xml_escape(self._onclick % json_dumps(args)),
470                     title=self._cw._(self._addmsg),
471                     Class='editableField'):
472              w(self._build_add_zone())
473 
474 -    def _edit_action(self, divid, args):
475 -        w = self.w
476 +    def _edit_action(self, w, divid, args):
477          args['action'] = 'edit-rtype'
478          with t.div(w, id='%s-update' % divid,
479                     onclick=xml_escape(self._onclick % json_dumps(args)),
480                     title=self._cw._(self._editzonemsg),
481                     Class='editableField'):
482              w(self._build_edit_zone())
483 
484 -    def view_form(self, divid, value, form=None, renderer=None, add_related=False, edit_rtype=True):
485 -        def _view_form():
486 -            args = form.event_args.copy()
487 -            if edit_rtype:
488 -                self._edit_action(divid, args)
489 -            if add_related:
490 -                self._add_action(divid, args)
491 -        self.wrap_form(divid, value, form, renderer, False, add_related, False, _view_form)
492 -
493 +    def view_relation_form(self, w, divid, value, form, renderer, add_related):
494 +        """
495 +        :divid: dom id if the surrounding reledit section
496 +        :value: display string of the edited relation
497 +        :form: relation form
498 +        :renderer: optional renderer of the form
499 +        :add_related: boolean controlling if a related entity widget can be built
500 +        :edit_or_add_form: callback building the edit/add_related entity ui actions
501 +        """
502 +        with t.div(w, id='%s-reledit' % divid,
503 +                   onmouseout="jQuery('#%s').addClass('hidden')" % divid,
504 +                   onmouseover="jQuery('#%s').removeClass('hidden')" % divid,
505 +                   Class='releditField'):
506 +            with t.div(w, id='%s-value' % divid, Class='editableFieldValue'):
507 +                w(value)
508 +            form.render(renderer=renderer, w=w)
509 +            with t.div(w, id=divid, Class='editableField hidden'):
510 +                args = form.event_args.copy()
511 +                if add_related:
512 +                    self._add_action(w, divid, args)
513 +                else:
514 +                    self._edit_action(w, divid, args)
515 
516  def registration_callback(vreg):
517      vreg.register_all(globals().values(), __name__)
518      vreg.unregister(old_cw_reledit.ClickAndEditFormView)
519      vreg.unregister(old_cw_reledit.AutoClickAndEditFormView)