1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
|
18 """This module (``cubicweb.web.views.uicfg``) regroups a set of structures that may be |
|
19 used to configure various options of the generated web interface. |
|
20 |
|
21 To configure the interface generation, we use ``RelationTag`` objects. |
|
22 |
|
23 Index view configuration |
|
24 ```````````````````````` |
|
25 :indexview_etype_section: |
|
26 entity type category in the index/manage page. May be one of: |
|
27 |
|
28 * ``application`` |
|
29 * ``system`` |
|
30 * ``schema`` |
|
31 * ``subobject`` (not displayed by default) |
|
32 |
|
33 By default only entities on the ``application`` category are shown. |
|
34 |
|
35 .. sourcecode:: python |
|
36 |
|
37 from cubicweb.web.views import uicfg |
|
38 # force hiding |
|
39 uicfg.indexview_etype_section['HideMe'] = 'subobject' |
|
40 # force display |
|
41 uicfg.indexview_etype_section['ShowMe'] = 'application' |
|
42 |
|
43 |
|
44 Actions box configuration |
|
45 ````````````````````````` |
|
46 :actionbox_appearsin_addmenu: |
|
47 simple boolean relation tags used to control the "add entity" submenu. |
|
48 Relations whose rtag is True will appears, other won't. |
|
49 |
|
50 .. sourcecode:: python |
|
51 |
|
52 # Adds all subjects of the entry_of relation in the add menu of the ``Blog`` |
|
53 # primary view |
|
54 uicfg.actionbox_appearsin_addmenu.tag_object_of(('*', 'entry_of', 'Blog'), True) |
|
55 """ |
|
56 __docformat__ = "restructuredtext en" |
|
57 |
|
58 from warnings import warn |
|
59 |
|
60 from six import string_types |
|
61 |
|
62 from cubicweb import neg_role |
|
63 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet, |
|
64 RelationTagsDict, NoTargetRelationTagsDict, |
|
65 _ensure_str_key) |
|
66 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES |
|
67 |
|
68 |
|
69 # primary view configuration ################################################## |
|
70 |
|
71 class PrimaryViewSectionRelationTags(RelationTags): |
|
72 """primary view section configuration""" |
|
73 __regid__ = 'primaryview_section' |
|
74 |
|
75 _allowed_values = frozenset(('attributes', 'relations', |
|
76 'sideboxes', 'hidden')) |
|
77 |
|
78 def _init(self, sschema, rschema, oschema, role): |
|
79 if self.get(sschema, rschema, oschema, role) is None: |
|
80 rdef = rschema.rdef(sschema, oschema) |
|
81 if rschema.final: |
|
82 if rschema.meta or sschema.is_metadata(rschema) \ |
|
83 or oschema.type in ('Password', 'Bytes'): |
|
84 section = 'hidden' |
|
85 else: |
|
86 section = 'attributes' |
|
87 else: |
|
88 if rdef.role_cardinality(role) in '1+': |
|
89 section = 'attributes' |
|
90 elif rdef.composite == neg_role(role): |
|
91 section = 'relations' |
|
92 else: |
|
93 section = 'sideboxes' |
|
94 self.tag_relation((sschema, rschema, oschema, role), section) |
|
95 |
|
96 primaryview_section = PrimaryViewSectionRelationTags() |
|
97 |
|
98 |
|
99 class DisplayCtrlRelationTags(NoTargetRelationTagsDict): |
|
100 """primary view display controller configuration""" |
|
101 __regid__ = 'primaryview_display_ctrl' |
|
102 |
|
103 def __init__(self, *args, **kwargs): |
|
104 super(DisplayCtrlRelationTags, self).__init__(*args, **kwargs) |
|
105 self.counter = 0 |
|
106 |
|
107 def _init(self, sschema, rschema, oschema, role): |
|
108 if role == 'subject': |
|
109 oschema = '*' |
|
110 else: |
|
111 sschema = '*' |
|
112 self.counter += 1 |
|
113 self.setdefault((sschema, rschema, oschema, role), |
|
114 'order', |
|
115 self.counter) |
|
116 |
|
117 def set_fields_order(self, etype, relations): |
|
118 """specify the field order in `etype` primary view. |
|
119 |
|
120 :param etype: the entity type as a string |
|
121 :param attrs: the ordered list of attribute names (or relations) |
|
122 |
|
123 `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel) |
|
124 |
|
125 Unspecified fields will be displayed after specified ones, their |
|
126 order being consistent with the schema definition. |
|
127 |
|
128 Examples: |
|
129 |
|
130 .. sourcecode:: python |
|
131 |
|
132 from cubicweb.web.views.uicfg import primaryview_display_ctrl as pvdc |
|
133 pvdc.set_fields_order('CWUser', ('firstname', ('in_group', 'subject'), |
|
134 'surname', 'login')) |
|
135 |
|
136 """ |
|
137 for index, relation in enumerate(relations): |
|
138 if not isinstance(relation, tuple): |
|
139 relation = (relation, 'subject') |
|
140 rtype, role = relation |
|
141 if role == 'subject': |
|
142 self.tag_subject_of((etype, rtype, '*'), {'order': index}) |
|
143 else: |
|
144 self.tag_object_of((etype, rtype, '*'), {'order': index}) |
|
145 |
|
146 |
|
147 primaryview_display_ctrl = DisplayCtrlRelationTags() |
|
148 |
|
149 |
|
150 # index view configuration #################################################### |
|
151 # entity type section in the index/manage page. May be one of |
|
152 # * 'application' |
|
153 # * 'system' |
|
154 # * 'schema' |
|
155 # * 'hidden' |
|
156 # * 'subobject' (not displayed by default) |
|
157 |
|
158 class InitializableDict(dict): # XXX not a rtag. Turn into an appobject? |
|
159 def __init__(self, *args, **kwargs): |
|
160 super(InitializableDict, self).__init__(*args, **kwargs) |
|
161 self.__defaults = dict(self) |
|
162 |
|
163 def init(self, schema, check=True): |
|
164 self.update(self.__defaults) |
|
165 for eschema in schema.entities(): |
|
166 if eschema.final: |
|
167 continue |
|
168 if eschema.schema_entity(): |
|
169 self.setdefault(eschema, 'schema') |
|
170 elif eschema in INTERNAL_TYPES or eschema in WORKFLOW_TYPES: |
|
171 self.setdefault(eschema, 'system') |
|
172 elif eschema.is_subobject(strict=True): |
|
173 self.setdefault(eschema, 'subobject') |
|
174 else: |
|
175 self.setdefault(eschema, 'application') |
|
176 |
|
177 indexview_etype_section = InitializableDict( |
|
178 EmailAddress='subobject', |
|
179 Bookmark='system', |
|
180 # entity types in the 'system' table by default (managers only) |
|
181 CWUser='system', CWGroup='system', |
|
182 ) |
|
183 |
|
184 |
|
185 # autoform.AutomaticEntityForm configuration ################################## |
|
186 |
|
187 def _formsections_as_dict(formsections): |
|
188 result = {} |
|
189 for formsection in formsections: |
|
190 formtype, section = formsection.split('_', 1) |
|
191 result[formtype] = section |
|
192 return result |
|
193 |
|
194 def _card_and_comp(sschema, rschema, oschema, role): |
|
195 rdef = rschema.rdef(sschema, oschema) |
|
196 if role == 'subject': |
|
197 card = rdef.cardinality[0] |
|
198 composed = not rschema.final and rdef.composite == 'object' |
|
199 else: |
|
200 card = rdef.cardinality[1] |
|
201 composed = not rschema.final and rdef.composite == 'subject' |
|
202 return card, composed |
|
203 |
|
204 class AutoformSectionRelationTags(RelationTagsSet): |
|
205 """autoform relations'section""" |
|
206 __regid__ = 'autoform_section' |
|
207 |
|
208 _allowed_form_types = ('main', 'inlined', 'muledit') |
|
209 _allowed_values = {'main': ('attributes', 'inlined', 'relations', |
|
210 'metadata', 'hidden'), |
|
211 'inlined': ('attributes', 'inlined', 'hidden'), |
|
212 'muledit': ('attributes', 'hidden'), |
|
213 } |
|
214 |
|
215 def init(self, schema, check=True): |
|
216 super(AutoformSectionRelationTags, self).init(schema, check) |
|
217 self.apply(schema, self._initfunc_step2) |
|
218 |
|
219 def _init(self, sschema, rschema, oschema, role): |
|
220 formsections = self.init_get(sschema, rschema, oschema, role) |
|
221 if formsections is None: |
|
222 formsections = self.tag_container_cls() |
|
223 if not any(tag.startswith('inlined') for tag in formsections): |
|
224 if not rschema.final: |
|
225 negsects = self.init_get(sschema, rschema, oschema, neg_role(role)) |
|
226 if 'main_inlined' in negsects: |
|
227 formsections.add('inlined_hidden') |
|
228 key = _ensure_str_key( (sschema, rschema, oschema, role) ) |
|
229 self._tagdefs[key] = formsections |
|
230 |
|
231 def _initfunc_step2(self, sschema, rschema, oschema, role): |
|
232 formsections = self.get(sschema, rschema, oschema, role) |
|
233 sectdict = _formsections_as_dict(formsections) |
|
234 if rschema in META_RTYPES: |
|
235 sectdict.setdefault('main', 'hidden') |
|
236 sectdict.setdefault('muledit', 'hidden') |
|
237 sectdict.setdefault('inlined', 'hidden') |
|
238 elif role == 'subject' and rschema in sschema.meta_attributes(): |
|
239 # meta attribute, usually embeded by the described attribute's field |
|
240 # (eg RichTextField, FileField...) |
|
241 sectdict.setdefault('main', 'hidden') |
|
242 sectdict.setdefault('muledit', 'hidden') |
|
243 sectdict.setdefault('inlined', 'hidden') |
|
244 # ensure we have a tag for each form type |
|
245 if not 'main' in sectdict: |
|
246 if not rschema.final and ( |
|
247 sectdict.get('inlined') == 'attributes' or |
|
248 'inlined_attributes' in self.init_get(sschema, rschema, oschema, |
|
249 neg_role(role))): |
|
250 sectdict['main'] = 'hidden' |
|
251 elif sschema.is_metadata(rschema): |
|
252 sectdict['main'] = 'metadata' |
|
253 else: |
|
254 card, composed = _card_and_comp(sschema, rschema, oschema, role) |
|
255 if card in '1+': |
|
256 sectdict['main'] = 'attributes' |
|
257 if not 'muledit' in sectdict: |
|
258 sectdict['muledit'] = 'attributes' |
|
259 elif rschema.final: |
|
260 sectdict['main'] = 'attributes' |
|
261 else: |
|
262 sectdict['main'] = 'relations' |
|
263 if not 'muledit' in sectdict: |
|
264 sectdict['muledit'] = 'hidden' |
|
265 if sectdict['main'] == 'attributes': |
|
266 card, composed = _card_and_comp(sschema, rschema, oschema, role) |
|
267 if card in '1+' and not composed: |
|
268 sectdict['muledit'] = 'attributes' |
|
269 if not 'inlined' in sectdict: |
|
270 sectdict['inlined'] = sectdict['main'] |
|
271 # recompute formsections and set it to avoid recomputing |
|
272 for formtype, section in sectdict.items(): |
|
273 formsections.add('%s_%s' % (formtype, section)) |
|
274 |
|
275 def tag_relation(self, key, formtype, section): |
|
276 if isinstance(formtype, tuple): |
|
277 for ftype in formtype: |
|
278 self.tag_relation(key, ftype, section) |
|
279 return |
|
280 assert formtype in self._allowed_form_types, \ |
|
281 'formtype should be in (%s), not %s' % ( |
|
282 ','.join(self._allowed_form_types), formtype) |
|
283 assert section in self._allowed_values[formtype], \ |
|
284 'section for %s should be in (%s), not %s' % ( |
|
285 formtype, ','.join(self._allowed_values[formtype]), section) |
|
286 rtags = self._tagdefs.setdefault(_ensure_str_key(key), |
|
287 self.tag_container_cls()) |
|
288 # remove previous section for this form type if any |
|
289 if rtags: |
|
290 for tag in rtags.copy(): |
|
291 if tag.startswith(formtype): |
|
292 rtags.remove(tag) |
|
293 rtags.add('%s_%s' % (formtype, section)) |
|
294 return rtags |
|
295 |
|
296 def init_get(self, stype, rtype, otype, tagged): |
|
297 key = (stype, rtype, otype, tagged) |
|
298 rtags = {} |
|
299 for key in self._get_keys(stype, rtype, otype, tagged): |
|
300 tags = self._tagdefs.get(key, ()) |
|
301 for tag in tags: |
|
302 assert '_' in tag, (tag, tags) |
|
303 section, value = tag.split('_', 1) |
|
304 rtags[section] = value |
|
305 cls = self.tag_container_cls |
|
306 rtags = cls('_'.join([section,value]) |
|
307 for section,value in rtags.items()) |
|
308 return rtags |
|
309 |
|
310 def get(self, *key): |
|
311 # overriden to avoid recomputing done in parent classes |
|
312 return self._tagdefs.get(key, ()) |
|
313 |
|
314 def relations_by_section(self, entity, formtype, section, permission, |
|
315 strict=False): |
|
316 """return a list of (relation schema, target schemas, role) for the |
|
317 given entity matching categories and permission. |
|
318 |
|
319 `strict`: |
|
320 bool telling if having local role is enough (strict = False) or not |
|
321 """ |
|
322 tag = '%s_%s' % (formtype, section) |
|
323 eschema = entity.e_schema |
|
324 cw = entity._cw |
|
325 permsoverrides = cw.vreg['uicfg'].select('autoform_permissions_overrides', cw, entity=entity) |
|
326 if entity.has_eid(): |
|
327 eid = entity.eid |
|
328 else: |
|
329 eid = None |
|
330 strict = False |
|
331 if permission == 'update': |
|
332 assert section in ('attributes', 'metadata', 'hidden') |
|
333 relpermission = 'add' |
|
334 else: |
|
335 assert section not in ('metadata', 'hidden') |
|
336 relpermission = permission |
|
337 for rschema, targetschemas, role in eschema.relation_definitions(True): |
|
338 _targetschemas = [] |
|
339 for tschema in targetschemas: |
|
340 # check section's tag first, potentially lower cost than |
|
341 # checking permission which may imply rql queries |
|
342 if not tag in self.etype_get(eschema, rschema, role, tschema): |
|
343 continue |
|
344 rdef = rschema.role_rdef(eschema, tschema, role) |
|
345 if rschema.final: |
|
346 if not rdef.has_perm(cw, permission, eid=eid, |
|
347 creating=eid is None): |
|
348 continue |
|
349 elif strict or not rdef.has_local_role(relpermission): |
|
350 if role == 'subject': |
|
351 if not rdef.has_perm(cw, relpermission, fromeid=eid): |
|
352 continue |
|
353 elif role == 'object': |
|
354 if not rdef.has_perm(cw, relpermission, toeid=eid): |
|
355 continue |
|
356 _targetschemas.append(tschema) |
|
357 if not _targetschemas: |
|
358 continue |
|
359 targetschemas = _targetschemas |
|
360 rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0]) |
|
361 # XXX tag allowing to hijack the permission machinery when |
|
362 # permission is not verifiable until the entity is actually |
|
363 # created... |
|
364 if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role): |
|
365 yield (rschema, targetschemas, role) |
|
366 continue |
|
367 if not rschema.final and role == 'subject': |
|
368 # on relation with cardinality 1 or ?, we need delete perm as well |
|
369 # if the relation is already set |
|
370 if (relpermission == 'add' |
|
371 and rdef.role_cardinality(role) in '1?' |
|
372 and eid and entity.related(rschema.type, role) |
|
373 and not rdef.has_perm(cw, 'delete', fromeid=eid, |
|
374 toeid=entity.related(rschema.type, role)[0][0])): |
|
375 continue |
|
376 elif role == 'object': |
|
377 # on relation with cardinality 1 or ?, we need delete perm as well |
|
378 # if the relation is already set |
|
379 if (relpermission == 'add' |
|
380 and rdef.role_cardinality(role) in '1?' |
|
381 and eid and entity.related(rschema.type, role) |
|
382 and not rdef.has_perm(cw, 'delete', toeid=eid, |
|
383 fromeid=entity.related(rschema.type, role)[0][0])): |
|
384 continue |
|
385 yield (rschema, targetschemas, role) |
|
386 |
|
387 def hide_field(self, etype, attr, desttype='*', formtype='main'): |
|
388 """hide `attr` in `etype` forms. |
|
389 |
|
390 :param etype: the entity type as a string |
|
391 :param attr: the name of the attribute or relation to hide |
|
392 :param formtype: which form will be affected ('main', 'inlined', etc.), |
|
393 *main* by default. |
|
394 |
|
395 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_rel) |
|
396 |
|
397 Examples: |
|
398 |
|
399 .. sourcecode:: python |
|
400 |
|
401 from cubicweb.web.views.uicfg import autoform_section as afs |
|
402 afs.hide_field('CWUser', 'login') |
|
403 afs.hide_field('*', 'name') |
|
404 afs.hide_field('CWUser', 'use_email', formtype='inlined') |
|
405 |
|
406 """ |
|
407 self._tag_etype_attr(etype, attr, desttype, |
|
408 formtype=formtype, section='hidden') |
|
409 |
|
410 def hide_fields(self, etype, attrs, formtype='main'): |
|
411 """simple for-loop wrapper around :func:`hide_field`. |
|
412 |
|
413 :param etype: the entity type as a string |
|
414 :param attrs: the ordered list of attribute names (or relations) |
|
415 :param formtype: which form will be affected ('main', 'inlined', etc.), |
|
416 *main* by default. |
|
417 |
|
418 `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel) |
|
419 |
|
420 Examples: |
|
421 |
|
422 .. sourcecode:: python |
|
423 |
|
424 from cubicweb.web.views.uicfg import autoform_section as afs |
|
425 afs.hide_fields('CWUser', ('login', ('use_email', 'subject')), |
|
426 formtype='inlined') |
|
427 """ |
|
428 for attr in attrs: |
|
429 self.hide_field(etype, attr, formtype=formtype) |
|
430 |
|
431 def edit_inline(self, etype, attr, desttype='*', formtype=('main', 'inlined')): |
|
432 """edit `attr` with and inlined form. |
|
433 |
|
434 :param etype: the entity type as a string |
|
435 :param attr: the name of the attribute or relation |
|
436 :param desttype: the destination type(s) concerned, default is everything |
|
437 :param formtype: which form will be affected ('main', 'inlined', etc.), |
|
438 *main* and *inlined* by default. |
|
439 |
|
440 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
441 |
|
442 Examples: |
|
443 |
|
444 .. sourcecode:: python |
|
445 |
|
446 from cubicweb.web.views.uicfg import autoform_section as afs |
|
447 |
|
448 afs.edit_inline('*', 'use_email') |
|
449 """ |
|
450 self._tag_etype_attr(etype, attr, desttype, formtype=formtype, |
|
451 section='inlined') |
|
452 |
|
453 def edit_as_attr(self, etype, attr, desttype='*', formtype=('main', 'muledit')): |
|
454 """make `attr` appear in the *attributes* section of `etype` form. |
|
455 |
|
456 :param etype: the entity type as a string |
|
457 :param attr: the name of the attribute or relation |
|
458 :param desttype: the destination type(s) concerned, default is everything |
|
459 :param formtype: which form will be affected ('main', 'inlined', etc.), |
|
460 *main* and *muledit* by default. |
|
461 |
|
462 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
463 |
|
464 Examples: |
|
465 |
|
466 .. sourcecode:: python |
|
467 |
|
468 from cubicweb.web.views.uicfg import autoform_section as afs |
|
469 |
|
470 afs.edit_as_attr('CWUser', 'in_group') |
|
471 """ |
|
472 self._tag_etype_attr(etype, attr, desttype, |
|
473 formtype=formtype, section='attributes') |
|
474 |
|
475 def set_muledit_editable(self, etype, attrs): |
|
476 """make `attrs` appear in muledit form of `etype`. |
|
477 |
|
478 :param etype: the entity type as a string |
|
479 :param attrs: the ordered list of attribute names (or relations) |
|
480 |
|
481 `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_relation) |
|
482 |
|
483 Examples: |
|
484 |
|
485 .. sourcecode:: python |
|
486 |
|
487 from cubicweb.web.views.uicfg import autoform_section as afs |
|
488 |
|
489 afs.set_muledit_editable('CWUser', ('firstname', 'surname', 'in_group')) |
|
490 """ |
|
491 for attr in attrs: |
|
492 self.edit_as_attr(self, etype, attr, formtype='muledit') |
|
493 |
|
494 autoform_section = AutoformSectionRelationTags() |
|
495 |
|
496 |
|
497 # relations'field class |
|
498 |
|
499 class AutoformFieldTags(RelationTags): |
|
500 __regid__ = 'autoform_field' |
|
501 |
|
502 def set_field(self, etype, attr, field): |
|
503 """sets the `attr` field of `etype`. |
|
504 |
|
505 :param etype: the entity type as a string |
|
506 :param attr: the name of the attribute or relation |
|
507 |
|
508 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
509 |
|
510 """ |
|
511 self._tag_etype_attr(etype, attr, '*', field) |
|
512 |
|
513 autoform_field = AutoformFieldTags() |
|
514 |
|
515 |
|
516 # relations'field explicit kwargs (given to field's __init__) |
|
517 |
|
518 class AutoformFieldKwargsTags(RelationTagsDict): |
|
519 __regid__ = 'autoform_field_kwargs' |
|
520 |
|
521 def set_fields_order(self, etype, attrs): |
|
522 """specify the field order in `etype` main edition form. |
|
523 |
|
524 :param etype: the entity type as a string |
|
525 :param attrs: the ordered list of attribute names (or relations) |
|
526 |
|
527 `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel) |
|
528 |
|
529 Unspecified fields will be displayed after specified ones, their |
|
530 order being consistent with the schema definition. |
|
531 |
|
532 Examples: |
|
533 |
|
534 .. sourcecode:: python |
|
535 |
|
536 from cubicweb.web.views.uicfg import autoform_field_kwargs as affk |
|
537 affk.set_fields_order('CWUser', ('firstname', 'surname', 'login')) |
|
538 affk.set_fields_order('CWUser', ('firstname', ('in_group', 'subject'), |
|
539 'surname', 'login')) |
|
540 |
|
541 """ |
|
542 for index, attr in enumerate(attrs): |
|
543 self._tag_etype_attr(etype, attr, '*', {'order': index}) |
|
544 |
|
545 def set_field_kwargs(self, etype, attr, **kwargs): |
|
546 """tag `attr` field of `etype` with additional named paremeters. |
|
547 |
|
548 :param etype: the entity type as a string |
|
549 :param attr: the name of the attribute or relation |
|
550 |
|
551 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
552 |
|
553 Examples: |
|
554 |
|
555 .. sourcecode:: python |
|
556 |
|
557 from cubicweb.web.views.uicfg import autoform_field_kwargs as affk |
|
558 affk.set_field_kwargs('Person', 'works_for', widget=fwdgs.AutoCompletionWidget()) |
|
559 affk.set_field_kwargs('CWUser', 'login', label=_('login or email address'), |
|
560 widget=fwdgs.TextInput(attrs={'size': 30})) |
|
561 """ |
|
562 self._tag_etype_attr(etype, attr, '*', kwargs) |
|
563 |
|
564 |
|
565 autoform_field_kwargs = AutoformFieldKwargsTags() |
|
566 |
|
567 |
|
568 # set of tags of the form <action>_on_new on relations. <action> is a |
|
569 # schema action (add/update/delete/read), and when such a tag is found |
|
570 # permissions checking is by-passed and supposed to be ok |
|
571 class AutoFormPermissionsOverrides(RelationTagsSet): |
|
572 __regid__ = 'autoform_permissions_overrides' |
|
573 |
|
574 autoform_permissions_overrides = AutoFormPermissionsOverrides() |
|
575 |
|
576 |
|
577 class ReleditTags(NoTargetRelationTagsDict): |
|
578 """Associate to relation a dictionary to control `reledit` (e.g. edition of |
|
579 attributes / relations from within views). |
|
580 |
|
581 Possible keys and associated values are: |
|
582 |
|
583 * `novalue_label`, alternative default value (shown when there is no value). |
|
584 |
|
585 * `novalue_include_rtype`, when `novalue_label` is not specified, this boolean |
|
586 flag control wether the generated default value should contains the |
|
587 relation label or not. Will be the opposite of the `showlabel` value found |
|
588 in the `primaryview_display_ctrl` rtag by default. |
|
589 |
|
590 * `reload`, boolean, eid (to reload to) or function taking subject and |
|
591 returning bool/eid. This is useful when editing a relation (or attribute) |
|
592 that impacts the url or another parts of the current displayed |
|
593 page. Defaults to False. |
|
594 |
|
595 * `rvid`, alternative view id (as str) for relation or composite edition. |
|
596 Default is 'autolimited'. |
|
597 |
|
598 * `edit_target`, may be either 'rtype' (to edit the relation) or 'related' |
|
599 (to edit the related entity). This controls whether to edit the relation |
|
600 or the target entity of the relation. Currently only one-to-one relations |
|
601 support target entity edition. By default, the 'related' option is taken |
|
602 whenever the relation is composite. |
|
603 """ |
|
604 __regid__ = 'reledit' |
|
605 _keys = frozenset('novalue_label novalue_include_rtype reload rvid edit_target'.split()) |
|
606 |
|
607 def tag_relation(self, key, tag): |
|
608 for tagkey in tag: |
|
609 assert tagkey in self._keys, 'tag %r not in accepted tags: %r' % (tag, self._keys) |
|
610 return super(ReleditTags, self).tag_relation(key, tag) |
|
611 |
|
612 def _init(self, sschema, rschema, oschema, role): |
|
613 values = self.get(sschema, rschema, oschema, role) |
|
614 if not rschema.final: |
|
615 composite = rschema.rdef(sschema, oschema).composite == role |
|
616 if role == 'subject': |
|
617 oschema = '*' |
|
618 else: |
|
619 sschema = '*' |
|
620 edittarget = values.get('edit_target') |
|
621 if edittarget not in (None, 'rtype', 'related'): |
|
622 self.warning('reledit: wrong value for edit_target on relation %s: %s', |
|
623 rschema, edittarget) |
|
624 edittarget = None |
|
625 if not edittarget: |
|
626 edittarget = 'related' if composite else 'rtype' |
|
627 self.tag_relation((sschema, rschema, oschema, role), |
|
628 {'edit_target': edittarget}) |
|
629 if not 'novalue_include_rtype' in values: |
|
630 showlabel = primaryview_display_ctrl.get( |
|
631 sschema, rschema, oschema, role).get('showlabel', True) |
|
632 self.tag_relation((sschema, rschema, oschema, role), |
|
633 {'novalue_include_rtype': not showlabel}) |
|
634 |
|
635 reledit_ctrl = ReleditTags() |
|
636 |
|
637 |
|
638 # boxes.EditBox configuration ################################################# |
|
639 |
|
640 # 'link' / 'create' relation tags, used to control the "add entity" submenu |
|
641 |
|
642 class ActionBoxUicfg(RelationTagsBool): |
|
643 __regid__ = 'actionbox_appearsin_addmenu' |
|
644 |
|
645 def _init(self, sschema, rschema, oschema, role): |
|
646 if self.get(sschema, rschema, oschema, role) is None: |
|
647 if rschema in META_RTYPES: |
|
648 self.tag_relation((sschema, rschema, oschema, role), False) |
|
649 return |
|
650 rdef = rschema.rdef(sschema, oschema) |
|
651 if not rdef.role_cardinality(role) in '?1' and rdef.composite == role: |
|
652 self.tag_relation((sschema, rschema, oschema, role), True) |
|
653 |
|
654 def _tag_etype_attr(self, etype, attr, desttype='*', *args, **kwargs): |
|
655 if isinstance(attr, string_types): |
|
656 attr, role = attr, 'subject' |
|
657 else: |
|
658 attr, role = attr |
|
659 if role == 'subject': |
|
660 self.tag_subject_of((etype, attr, desttype), *args, **kwargs) |
|
661 else: |
|
662 self.tag_object_of((desttype, attr, etype), *args, **kwargs) |
|
663 |
|
664 def append_to_addmenu(self, etype, attr, createdtype='*'): |
|
665 """adds `attr` in the actions box *addrelated* submenu of `etype`. |
|
666 |
|
667 :param etype: the entity type as a string |
|
668 :param attr: the name of the attribute or relation to hide |
|
669 :param createdtype: the target type of the relation (optional, defaults to '*' (all possible types)) |
|
670 |
|
671 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
672 |
|
673 """ |
|
674 self._tag_etype_attr(etype, attr, createdtype, True) |
|
675 |
|
676 def remove_from_addmenu(self, etype, attr, createdtype='*'): |
|
677 """removes `attr` from the actions box *addrelated* submenu of `etype`. |
|
678 |
|
679 :param etype: the entity type as a string |
|
680 :param attr: the name of the attribute or relation to hide |
|
681 :param createdtype: the target type of the relation (optional, defaults to '*' (all possible types)) |
|
682 |
|
683 `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation) |
|
684 """ |
|
685 self._tag_etype_attr(etype, attr, createdtype, False) |
|
686 |
|
687 actionbox_appearsin_addmenu = ActionBoxUicfg() |
|
688 |
|
689 |
|
690 |
|
691 def registration_callback(vreg): |
|
692 vreg.register_all(globals().values(), __name__) |
|
693 indexview_etype_section.init(vreg.schema) |
|