258 self.w(form.render()) |
259 self.w(form.render()) |
259 |
260 |
260 |
261 |
261 # click and edit handling ('reledit') ########################################## |
262 # click and edit handling ('reledit') ########################################## |
262 |
263 |
263 class DummyForm(object): |
264 ClickAndEditFormView = class_moved(reledit.ClickAndEditFormView) |
264 __slots__ = ('event_args',) |
265 AutoClickAndEditFormView = class_moved(reledit.AutoClickAndEditFormView) |
265 def form_render(self, **_args): |
|
266 return u'' |
|
267 def render(self, **_args): |
|
268 return u'' |
|
269 def append_field(self, *args): |
|
270 pass |
|
271 def field_by_name(self, rtype, role, eschema=None): |
|
272 return None |
|
273 |
|
274 |
|
275 class ClickAndEditFormView(FormViewMixIn, EntityView): |
|
276 """form used to permit ajax edition of a relation or attribute of an entity |
|
277 in a view, if logged user have the permission to edit it. |
|
278 |
|
279 (double-click on the field to see an appropriate edition widget). |
|
280 """ |
|
281 __regid__ = 'doreledit' |
|
282 __select__ = non_final_entity() & match_kwargs('rtype') |
|
283 # FIXME editableField class could be toggleable from userprefs |
|
284 |
|
285 _onclick = u"showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')" |
|
286 _onsubmit = ("return inlineValidateRelationFormOptions('%(rtype)s', '%(eid)s', " |
|
287 "'%(divid)s', %(options)s);") |
|
288 _cancelclick = "hideInlineEdit(%s,\'%s\',\'%s\')" |
|
289 _defaultlandingzone = (u'<img title="%(msg)s" src="data/pen_icon.png" ' |
|
290 'alt="%(msg)s"/>') |
|
291 _landingzonemsg = _('click to edit this field') |
|
292 # default relation vids according to cardinality |
|
293 _one_rvid = 'incontext' |
|
294 _many_rvid = 'csv' |
|
295 |
|
296 |
|
297 def cell_call(self, row, col, rtype=None, role='subject', |
|
298 reload=False, # controls reloading the whole page after change |
|
299 rvid=None, # vid to be applied to other side of rtype (non final relations only) |
|
300 default=None, # default value |
|
301 landing_zone=None # prepend value with a separate html element to click onto |
|
302 # (esp. needed when values are links) |
|
303 ): |
|
304 """display field to edit entity's `rtype` relation on click""" |
|
305 assert rtype |
|
306 assert role in ('subject', 'object'), '%s is not an acceptable role value' % role |
|
307 self._cw.add_js('cubicweb.edition.js') |
|
308 self._cw.add_css('cubicweb.form.css') |
|
309 if default is None: |
|
310 default = xml_escape(self._cw._('<%s not specified>') |
|
311 % display_name(self._cw, rtype, role)) |
|
312 schema = self._cw.vreg.schema |
|
313 entity = self.cw_rset.get_entity(row, col) |
|
314 rschema = schema.rschema(rtype) |
|
315 lzone = self._build_landing_zone(landing_zone) |
|
316 # compute value, checking perms, build form |
|
317 if rschema.final: |
|
318 form = self._build_form(entity, rtype, role, 'base', default, reload, lzone) |
|
319 if not self.should_edit_attribute(entity, rschema, form): |
|
320 self.w(entity.printable_value(rtype)) |
|
321 return |
|
322 value = entity.printable_value(rtype) or default |
|
323 else: |
|
324 rvid = self._compute_best_vid(entity.e_schema, rschema, role) |
|
325 rset = entity.related(rtype, role) |
|
326 if rset: |
|
327 value = self._cw.view(rvid, rset) |
|
328 else: |
|
329 value = default |
|
330 if not self.should_edit_relation(entity, rschema, role, rvid): |
|
331 if rset: |
|
332 self.w(value) |
|
333 return |
|
334 # XXX do we really have to give lzone twice? |
|
335 form = self._build_form(entity, rtype, role, 'base', default, reload, lzone, |
|
336 dict(vid=rvid, lzone=lzone)) |
|
337 field = form.field_by_name(rtype, role, entity.e_schema) |
|
338 form.append_field(field) |
|
339 self.relation_form(lzone, value, form, |
|
340 self._build_renderer(entity, rtype, role)) |
|
341 |
|
342 def should_edit_attribute(self, entity, rschema, form): |
|
343 if not entity.cw_has_perm('update'): |
|
344 return False |
|
345 rdef = entity.e_schema.rdef(rschema) |
|
346 if not rdef.has_perm(self._cw, 'update', eid=entity.eid): |
|
347 return False |
|
348 try: |
|
349 form.field_by_name(str(rschema), 'subject', entity.e_schema) |
|
350 except FieldNotFound: |
|
351 return False |
|
352 return True |
|
353 |
|
354 def should_edit_relation(self, entity, rschema, role, rvid): |
|
355 if ((role == 'subject' and not rschema.has_perm(self._cw, 'add', |
|
356 fromeid=entity.eid)) |
|
357 or |
|
358 (role == 'object' and not rschema.has_perm(self._cw, 'add', |
|
359 toeid=entity.eid))): |
|
360 return False |
|
361 return True |
|
362 |
|
363 def relation_form(self, lzone, value, form, renderer): |
|
364 """xxx-reledit div (class=field) |
|
365 +-xxx div (class="editableField") |
|
366 | +-landing zone |
|
367 +-xxx-value div |
|
368 +-xxx-form div |
|
369 """ |
|
370 w = self.w |
|
371 divid = form.event_args['divid'] |
|
372 w(u'<div id="%s-reledit" class="field" ' |
|
373 u'onmouseout="jQuery(\'#%s\').addClass(\'hidden\')" ' |
|
374 u'onmouseover="jQuery(\'#%s\').removeClass(\'hidden\')">' |
|
375 % (divid, divid, divid)) |
|
376 w(u'<div id="%s-value" class="editableFieldValue">%s</div>' % (divid, value)) |
|
377 w(form.render(renderer=renderer)) |
|
378 w(u'<div id="%s" class="editableField hidden" onclick="%s" title="%s">' % ( |
|
379 divid, xml_escape(self._onclick % form.event_args), |
|
380 self._cw._(self._landingzonemsg))) |
|
381 w(lzone) |
|
382 w(u'</div>') |
|
383 w(u'</div>') |
|
384 |
|
385 def _compute_best_vid(self, eschema, rschema, role): |
|
386 dispctrl = _pvdc.etype_get(eschema, rschema, role) |
|
387 if dispctrl.get('rvid'): |
|
388 return dispctrl['rvid'] |
|
389 if eschema.rdef(rschema, role).role_cardinality(role) in '+*': |
|
390 return self._many_rvid |
|
391 return self._one_rvid |
|
392 |
|
393 def _build_landing_zone(self, lzone): |
|
394 return lzone or self._defaultlandingzone % { |
|
395 'msg': xml_escape(self._cw._(self._landingzonemsg))} |
|
396 |
|
397 def _build_renderer(self, entity, rtype, role): |
|
398 return self._cw.vreg['formrenderers'].select( |
|
399 'base', self._cw, entity=entity, display_label=False, |
|
400 display_help=False, table_class='', |
|
401 button_bar_class='buttonbar', display_progress_div=False) |
|
402 |
|
403 def _build_args(self, entity, rtype, role, formid, default, reload, lzone, |
|
404 extradata=None): |
|
405 divid = '%s-%s-%s' % (rtype, role, entity.eid) |
|
406 options = {'reload' : reload, 'default_value' : default, |
|
407 'role' : role, 'vid' : '', |
|
408 'lzone' : lzone} |
|
409 event_args = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, |
|
410 'options' : dumps(options)} |
|
411 if extradata: |
|
412 event_args.update(extradata) |
|
413 return divid, event_args |
|
414 |
|
415 def _build_form(self, entity, rtype, role, formid, default, reload, lzone, |
|
416 extradata=None, **formargs): |
|
417 divid, event_args = self._build_args(entity, rtype, role, formid, default, |
|
418 reload, lzone, extradata) |
|
419 onsubmit = self._onsubmit % event_args |
|
420 cancelclick = self._cancelclick % (entity.eid, rtype, divid) |
|
421 form = self._cw.vreg['forms'].select( |
|
422 formid, self._cw, entity=entity, domid='%s-form' % divid, |
|
423 cssstyle='display: none', onsubmit=onsubmit, action='#', |
|
424 form_buttons=[fw.SubmitButton(), |
|
425 fw.Button(stdmsgs.BUTTON_CANCEL, onclick=cancelclick)], |
|
426 **formargs) |
|
427 form.event_args = event_args |
|
428 return form |
|
429 |
|
430 |
|
431 class AutoClickAndEditFormView(ClickAndEditFormView): |
|
432 """same as ClickAndEditFormView but checking if the view *should* be applied |
|
433 by checking uicfg configuration and composite relation property. |
|
434 """ |
|
435 __regid__ = 'reledit' |
|
436 _onclick = (u"loadInlineEditionFormOptions(%(eid)s, '%(rtype)s', " |
|
437 "'%(divid)s', %(options)s);") |
|
438 |
|
439 def should_edit_attribute(self, entity, rschema, form): |
|
440 rdef = entity.e_schema.rdef(rschema) |
|
441 afs = uicfg.autoform_section.etype_get( |
|
442 entity.__regid__, rschema, 'subject', rdef.object) |
|
443 if 'main_hidden' in afs: |
|
444 return False |
|
445 return super(AutoClickAndEditFormView, self).should_edit_attribute( |
|
446 entity, rschema, form) |
|
447 |
|
448 def should_edit_relation(self, entity, rschema, role, rvid): |
|
449 eschema = entity.e_schema |
|
450 dispctrl = _pvdc.etype_get(eschema, rschema, role) |
|
451 vid = dispctrl.get('vid', 'reledit') |
|
452 if vid != 'reledit': # reledit explicitly disabled |
|
453 return False |
|
454 rdef = eschema.rdef(rschema, role) |
|
455 if rdef.composite == role: |
|
456 return False |
|
457 afs = uicfg.autoform_section.etype_get( |
|
458 entity.__regid__, rschema, role, rdef.object) |
|
459 if 'main_hidden' in afs: |
|
460 return False |
|
461 return super(AutoClickAndEditFormView, self).should_edit_relation( |
|
462 entity, rschema, role, rvid) |
|
463 |
|
464 def _build_form(self, entity, rtype, role, formid, default, reload, lzone, |
|
465 extradata=None, **formargs): |
|
466 _divid, event_args = self._build_args(entity, rtype, role, formid, default, |
|
467 reload, lzone, extradata) |
|
468 form = DummyForm() |
|
469 form.event_args = event_args |
|
470 return form |
|
471 |
|
472 def _build_renderer(self, entity, rtype, role): |
|
473 pass |
|
474 |
|