15 from cubicweb import typed_eid |
15 from cubicweb import typed_eid |
16 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
16 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
17 from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param |
17 from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param |
18 from cubicweb.web import form, formwidgets as fwdgs |
18 from cubicweb.web import form, formwidgets as fwdgs |
19 from cubicweb.web.controller import NAV_FORM_PARAMETERS |
19 from cubicweb.web.controller import NAV_FORM_PARAMETERS |
20 from cubicweb.web.formfields import StringField |
20 from cubicweb.web.formfields import StringField, relvoc_unrelated |
21 |
21 |
22 |
22 |
23 class FieldsForm(form.Form): |
23 class FieldsForm(form.Form): |
24 """base class for fields based forms. |
24 """base class for fields based forms. |
25 |
25 |
171 return self._cw.property_value('ui.default-text-format') |
171 return self._cw.property_value('ui.default-text-format') |
172 |
172 |
173 def form_field_encoding(self, field): |
173 def form_field_encoding(self, field): |
174 """return encoding used for the given (text) field""" |
174 """return encoding used for the given (text) field""" |
175 return self._cw.encoding |
175 return self._cw.encoding |
176 |
|
177 def form_field_vocabulary(self, field, limit=None): |
|
178 """return vocabulary for the given field. Should be overriden in |
|
179 specific forms using fields which requires some vocabulary |
|
180 """ |
|
181 raise NotImplementedError |
|
182 |
176 |
183 def form_field_modified(self, field): |
177 def form_field_modified(self, field): |
184 return field.is_visible() |
178 return field.is_visible() |
185 |
179 |
186 def _field_has_error(self, field): |
180 def _field_has_error(self, field): |
255 """return true if the field has some error in given validation exception |
249 """return true if the field has some error in given validation exception |
256 """ |
250 """ |
257 return super(EntityFieldsForm, self)._field_has_error(field) \ |
251 return super(EntityFieldsForm, self)._field_has_error(field) \ |
258 and self.form_valerror.eid == self.edited_entity.eid |
252 and self.form_valerror.eid == self.edited_entity.eid |
259 |
253 |
260 def _relation_vocabulary(self, rtype, targettype, role, |
|
261 limit=None, done=None): |
|
262 """return unrelated entities for a given relation and target entity type |
|
263 for use in vocabulary |
|
264 """ |
|
265 if done is None: |
|
266 done = set() |
|
267 rset = self.edited_entity.unrelated(rtype, targettype, role, limit) |
|
268 res = [] |
|
269 for entity in rset.entities(): |
|
270 if entity.eid in done: |
|
271 continue |
|
272 done.add(entity.eid) |
|
273 res.append((entity.view('combobox'), entity.eid)) |
|
274 return res |
|
275 |
|
276 def _req_display_value(self, field): |
|
277 value = super(EntityFieldsForm, self)._req_display_value(field) |
|
278 if value is None: |
|
279 value = self.edited_entity.linked_to(field.name, field.role) |
|
280 if value: |
|
281 searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role) |
|
282 for eid in value] |
|
283 # remove associated __linkto hidden fields |
|
284 for field in self.root_form.fields_by_name('__linkto'): |
|
285 if field.initial in searchedvalues: |
|
286 self.root_form.remove_field(field) |
|
287 else: |
|
288 value = None |
|
289 return value |
|
290 |
|
291 def _form_field_default_value(self, field, load_bytes): |
|
292 defaultattr = 'default_%s' % field.name |
|
293 if hasattr(self.edited_entity, defaultattr): |
|
294 # XXX bw compat, default_<field name> on the entity |
|
295 warn('found %s on %s, should be set on a specific form' |
|
296 % (defaultattr, self.edited_entity.__regid__), DeprecationWarning) |
|
297 value = getattr(self.edited_entity, defaultattr) |
|
298 if callable(value): |
|
299 value = value() |
|
300 else: |
|
301 value = super(EntityFieldsForm, self).form_field_value(field, |
|
302 load_bytes) |
|
303 return value |
|
304 |
|
305 def form_default_renderer(self): |
|
306 return self._cw.vreg['formrenderers'].select( |
254 return self._cw.vreg['formrenderers'].select( |
307 self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row, |
255 self.form_renderer_id, self._cw, rset=self.cw_rset, row=self.cw_row, |
308 col=self.cw_col, entity=self.edited_entity) |
256 col=self.cw_col, entity=self.edited_entity) |
309 |
257 |
310 |
258 |
323 if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and ( |
271 if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and ( |
324 entity.has_eid() or '%s_encoding' % field.name in entity): |
272 entity.has_eid() or '%s_encoding' % field.name in entity): |
325 return self.edited_entity.attr_metadata(field.name, 'encoding') |
273 return self.edited_entity.attr_metadata(field.name, 'encoding') |
326 return super(EntityFieldsForm, self).form_field_encoding(field) |
274 return super(EntityFieldsForm, self).form_field_encoding(field) |
327 # XXX all this vocabulary handling should be on the field, no? |
275 # XXX all this vocabulary handling should be on the field, no? |
328 |
|
329 def form_field_vocabulary(self, field, limit=None): |
|
330 """return vocabulary for the given field""" |
|
331 role, rtype = field.role, field.name |
|
332 method = '%s_%s_vocabulary' % (role, rtype) |
|
333 def actual_eid(self, eid): |
276 def actual_eid(self, eid): |
334 # should be either an int (existant entity) or a variable (to be |
277 # should be either an int (existant entity) or a variable (to be |
335 # created entity) |
278 # created entity) |
336 assert eid or eid == 0, repr(eid) # 0 is a valid eid |
279 assert eid or eid == 0, repr(eid) # 0 is a valid eid |
337 try: |
280 try: |
338 vocabfunc = getattr(self, method) |
|
339 except AttributeError: |
|
340 return typed_eid(eid) |
281 return typed_eid(eid) |
341 except ValueError: |
282 except ValueError: |
342 try: |
283 try: |
343 # XXX bw compat, <role>_<rtype>_vocabulary on the entity |
|
344 vocabfunc = getattr(self.edited_entity, method) |
|
345 except AttributeError: |
|
346 vocabfunc = getattr(self, '%s_relation_vocabulary' % role) |
|
347 else: |
|
348 warn('found %s on %s, should be set on a specific form' |
|
349 % (method, self.edited_entity.__regid__), DeprecationWarning) |
|
350 # NOTE: it is the responsibility of `vocabfunc` to sort the result |
|
351 # (direclty through RQL or via a python sort). This is also |
|
352 # important because `vocabfunc` might return a list with |
|
353 # couples (label, None) which act as separators. In these |
|
354 # cases, it doesn't make sense to sort results afterwards. |
|
355 return vocabfunc(rtype, limit) |
|
356 |
284 |
357 def form_field_modified(self, field): |
285 def form_field_modified(self, field): |
358 if field.is_visible(): |
286 if field.is_visible(): |
359 # fields not corresponding to an entity attribute / relations |
287 # fields not corresponding to an entity attribute / relations |
360 # are considered modified |
288 # are considered modified |
377 new_value = field.process_form_value(self) |
305 new_value = field.process_form_value(self) |
378 if self.edited_entity.has_eid() and (previous_value == new_value): |
306 if self.edited_entity.has_eid() and (previous_value == new_value): |
379 return False # not modified |
307 return False # not modified |
380 return True |
308 return True |
381 return False |
309 return False |
382 |
|
383 def subject_relation_vocabulary(self, rtype, limit=None): |
|
384 """defaut vocabulary method for the given relation, looking for |
|
385 relation's object entities (i.e. self is the subject) |
|
386 """ |
|
387 entity = self.edited_entity |
|
388 if isinstance(rtype, basestring): |
|
389 rtype = self._cw.vreg.schema.rschema(rtype) |
|
390 done = None |
|
391 assert not rtype.final, rtype |
|
392 if entity.has_eid(): |
|
393 done = set(e.eid for e in getattr(entity, str(rtype))) |
|
394 result = [] |
|
395 rsetsize = None |
|
396 for objtype in rtype.objects(entity.e_schema): |
|
397 if limit is not None: |
|
398 rsetsize = limit - len(result) |
|
399 result += self._relation_vocabulary(rtype, objtype, 'subject', |
|
400 rsetsize, done) |
|
401 if limit is not None and len(result) >= limit: |
|
402 break |
|
403 return result |
|
404 |
|
405 def object_relation_vocabulary(self, rtype, limit=None): |
|
406 """defaut vocabulary method for the given relation, looking for |
|
407 relation's subject entities (i.e. self is the object) |
|
408 """ |
|
409 entity = self.edited_entity |
|
410 if isinstance(rtype, basestring): |
|
411 rtype = self._cw.vreg.schema.rschema(rtype) |
|
412 done = None |
|
413 if entity.has_eid(): |
|
414 done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype)) |
|
415 result = [] |
|
416 rsetsize = None |
|
417 for subjtype in rtype.subjects(entity.e_schema): |
|
418 if limit is not None: |
|
419 rsetsize = limit - len(result) |
|
420 result += self._relation_vocabulary(rtype, subjtype, 'object', |
|
421 rsetsize, done) |
|
422 if limit is not None and len(result) >= limit: |
|
423 break |
|
424 return result |
|
425 return self._cw.data['eidmap'][eid] |
310 return self._cw.data['eidmap'][eid] |
426 except KeyError: |
311 except KeyError: |
427 self._cw.data['eidmap'][eid] = None |
312 self._cw.data['eidmap'][eid] = None |
428 return None |
313 return None |
429 |
314 |
431 return () |
316 return () |
432 |
317 |
433 def should_display_add_new_relation_link(self, rschema, existant, card): |
318 def should_display_add_new_relation_link(self, rschema, existant, card): |
434 return False |
319 return False |
435 |
320 |
|
321 @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function') |
|
322 def subject_relation_vocabulary(self, rtype, limit=None): |
|
323 """defaut vocabulary method for the given relation, looking for |
|
324 relation's object entities (i.e. self is the subject) |
|
325 """ |
|
326 return relvoc_unrelated(self.edited_entity, rtype, 'subject', limit=None) |
|
327 |
|
328 @deprecated('[3.6] use cw.web.formfields.relvoc_unrelated function') |
|
329 def object_relation_vocabulary(self, rtype, limit=None): |
|
330 return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None) |
|
331 |
436 |
332 |
437 class CompositeFormMixIn(object): |
333 class CompositeFormMixIn(object): |
438 """form composed of sub-forms""" |
334 """form composed of sub-forms""" |
439 __regid__ = 'composite' |
335 __regid__ = 'composite' |
440 form_renderer_id = __regid__ |
336 form_renderer_id = __regid__ |