45 __docformat__ = "restructuredtext en" |
45 __docformat__ = "restructuredtext en" |
46 |
46 |
47 from warnings import warn |
47 from warnings import warn |
48 |
48 |
49 from logilab.common import dictattr, tempattr |
49 from logilab.common import dictattr, tempattr |
50 from logilab.common.decorators import iclassmethod |
50 from logilab.common.decorators import iclassmethod, cached |
51 from logilab.common.compat import any |
51 from logilab.common.compat import any |
52 from logilab.common.textutils import splitstrip |
52 from logilab.common.textutils import splitstrip |
53 from logilab.common.deprecation import deprecated |
53 from logilab.common.deprecation import deprecated |
54 |
54 |
55 from cubicweb import ValidationError, typed_eid |
55 from cubicweb import ValidationError, typed_eid |
56 from cubicweb.utils import support_args |
56 from cubicweb.utils import support_args |
57 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
57 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
58 from cubicweb.web import RequestError, ProcessFormError |
58 from cubicweb.web import RequestError, ProcessFormError |
59 from cubicweb.web import uicfg, form, formwidgets as fwdgs |
59 from cubicweb.web import uicfg, form, formwidgets as fwdgs |
60 from cubicweb.web.formfields import relvoc_unrelated, guess_field |
60 from cubicweb.web.formfields import guess_field |
61 |
61 |
62 |
62 |
63 class FieldsForm(form.Form): |
63 class FieldsForm(form.Form): |
64 """This is the base class for fields based forms. |
64 """This is the base class for fields based forms. |
65 |
65 |
180 if self.needs_js: |
180 if self.needs_js: |
181 self._cw.add_js(self.needs_js) |
181 self._cw.add_js(self.needs_js) |
182 if self.needs_css: |
182 if self.needs_css: |
183 self._cw.add_css(self.needs_css) |
183 self._cw.add_css(self.needs_css) |
184 |
184 |
185 def render(self, formvalues=None, rendervalues=None, renderer=None, **kwargs): |
185 def render(self, formvalues=None, renderer=None, **kwargs): |
186 """Render this form, using the `renderer` given as argument or the |
186 """Render this form, using the `renderer` given as argument or the |
187 default according to :attr:`form_renderer_id`. The rendered form is |
187 default according to :attr:`form_renderer_id`. The rendered form is |
188 returned as an unicode string. |
188 returned as an unicode string. |
189 |
189 |
190 `formvalues` is an optional dictionary containing values that will be |
190 `formvalues` is an optional dictionary containing values that will be |
191 considered as field's value. |
191 considered as field's value. |
192 |
192 |
193 Extra keyword arguments will be given to renderer's :meth:`render` method. |
193 Extra keyword arguments will be given to renderer's :meth:`render` method. |
194 |
194 """ |
195 `rendervalues` is deprecated. |
|
196 """ |
|
197 if rendervalues is not None: |
|
198 warn('[3.6] rendervalues argument is deprecated, all named arguments will be given instead', |
|
199 DeprecationWarning, stacklevel=2) |
|
200 kwargs = rendervalues |
|
201 w = kwargs.pop('w', None) |
195 w = kwargs.pop('w', None) |
202 if w is None: |
196 if w is None: |
203 warn('[3.10] you should specify "w" to form.render() named arguments', |
197 warn('[3.10] you should specify "w" to form.render() named arguments', |
204 DeprecationWarning, stacklevel=2) |
198 DeprecationWarning, stacklevel=2) |
205 data = [] |
199 data = [] |
304 if errors: |
298 if errors: |
305 errors = dict((f.role_name(), unicode(ex)) for f, ex in errors) |
299 errors = dict((f.role_name(), unicode(ex)) for f, ex in errors) |
306 raise ValidationError(None, errors) |
300 raise ValidationError(None, errors) |
307 return processed |
301 return processed |
308 |
302 |
309 @deprecated('[3.6] use .add_hidden(name, value, **kwargs)') |
|
310 def form_add_hidden(self, name, value=None, **kwargs): |
|
311 return self.add_hidden(name, value, **kwargs) |
|
312 |
|
313 @deprecated('[3.6] use .render(formvalues, **rendervalues)') |
|
314 def form_render(self, **values): |
|
315 """render this form, using the renderer given in args or the default |
|
316 FormRenderer() |
|
317 """ |
|
318 self.build_context(values) |
|
319 renderer = values.pop('renderer', None) |
|
320 if renderer is None: |
|
321 renderer = self.default_renderer() |
|
322 return renderer.render(self, values) |
|
323 |
|
324 |
303 |
325 _AFF = uicfg.autoform_field |
304 _AFF = uicfg.autoform_field |
326 _AFF_KWARGS = uicfg.autoform_field_kwargs |
305 _AFF_KWARGS = uicfg.autoform_field_kwargs |
327 |
306 |
328 class EntityFieldsForm(FieldsForm): |
307 class EntityFieldsForm(FieldsForm): |
374 self.add_hidden('eid', self.edited_entity.eid) |
353 self.add_hidden('eid', self.edited_entity.eid) |
375 # mainform default to true in parent, hence default to True |
354 # mainform default to true in parent, hence default to True |
376 if kwargs.get('mainform', True) or kwargs.get('mainentity', False): |
355 if kwargs.get('mainform', True) or kwargs.get('mainentity', False): |
377 self.add_hidden(u'__maineid', self.edited_entity.eid) |
356 self.add_hidden(u'__maineid', self.edited_entity.eid) |
378 # If we need to directly attach the new object to another one |
357 # If we need to directly attach the new object to another one |
379 if self._cw.list_form_param('__linkto'): |
358 if '__linkto' in self._cw.form: |
380 for linkto in self._cw.list_form_param('__linkto'): |
|
381 self.add_hidden('__linkto', linkto) |
|
382 if msg: |
359 if msg: |
383 msg = '%s %s' % (msg, self._cw._('and linked')) |
360 msg = '%s %s' % (msg, self._cw._('and linked')) |
384 else: |
361 else: |
385 msg = self._cw._('entity linked') |
362 msg = self._cw._('entity linked') |
386 if msg: |
363 if msg: |
387 msgid = self._cw.set_redirect_message(msg) |
364 msgid = self._cw.set_redirect_message(msg) |
388 self.add_hidden('_cwmsgid', msgid) |
365 self.add_hidden('_cwmsgid', msgid) |
|
366 |
|
367 def add_linkto_hidden(self): |
|
368 """add the __linkto hidden field used to directly attach the new object |
|
369 to an existing other one when the relation between those two is not |
|
370 already present in the form. |
|
371 |
|
372 Warning: this method must be called only when all form fields are setup |
|
373 """ |
|
374 for (rtype, role), eids in self.linked_to.iteritems(): |
|
375 # if the relation is already setup by a form field, do not add it |
|
376 # in a __linkto hidden to avoid setting it twice in the controller |
|
377 try: |
|
378 self.field_by_name(rtype, role) |
|
379 except form.FieldNotFound: |
|
380 for eid in eids: |
|
381 self.add_hidden('__linkto', '%s:%s:%s' % (rtype, eid, role)) |
|
382 |
|
383 def render(self, *args, **kwargs): |
|
384 self.add_linkto_hidden() |
|
385 return super(EntityFieldsForm, self).render(*args, **kwargs) |
|
386 |
|
387 @property |
|
388 @cached |
|
389 def linked_to(self): |
|
390 # if current form is not the main form, exit immediately |
|
391 try: |
|
392 self.field_by_name('__maineid') |
|
393 except form.FieldNotFound: |
|
394 return {} |
|
395 linked_to = {} |
|
396 for linkto in self._cw.list_form_param('__linkto'): |
|
397 ltrtype, eid, ltrole = linkto.split(':') |
|
398 linked_to.setdefault((ltrtype, ltrole), []).append(typed_eid(eid)) |
|
399 return linked_to |
389 |
400 |
390 def session_key(self): |
401 def session_key(self): |
391 """return the key that may be used to store / retreive data about a |
402 """return the key that may be used to store / retreive data about a |
392 previous post which failed because of a validation error |
403 previous post which failed because of a validation error |
393 """ |
404 """ |
425 return None |
436 return None |
426 |
437 |
427 def editable_relations(self): |
438 def editable_relations(self): |
428 return () |
439 return () |
429 |
440 |
430 @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function') |
|
431 def subject_relation_vocabulary(self, rtype, limit=None): |
|
432 """defaut vocabulary method for the given relation, looking for |
|
433 relation's object entities (i.e. self is the subject) |
|
434 """ |
|
435 return relvoc_unrelated(self.edited_entity, rtype, 'subject', limit=None) |
|
436 |
|
437 @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function') |
|
438 def object_relation_vocabulary(self, rtype, limit=None): |
|
439 return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None) |
|
440 |
|
441 |
441 |
442 class CompositeFormMixIn(object): |
442 class CompositeFormMixIn(object): |
443 __regid__ = 'composite' |
443 __regid__ = 'composite' |
444 form_renderer_id = __regid__ |
444 form_renderer_id = __regid__ |
445 |
445 |