1 """some base form classes for CubicWeb web client |
1 # organization: Logilab |
2 |
2 # copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
3 :organization: Logilab |
3 # contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
4 # license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 """ |
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
6 Base form classes |
|
7 ----------------- |
|
8 |
|
9 .. Note: |
|
10 |
|
11 Form is the glue that bind a context to a set of fields, and is rendered |
|
12 using a form renderer. No display is actually done here, though you'll find |
|
13 some attributes of form that are used to control the rendering process. |
|
14 |
|
15 Besides the automagic form we'll see later, they are barely two form |
|
16 classes in |cubicweb|: |
|
17 |
|
18 .. autoclass:: cubicweb.web.views.forms.FieldsForm |
|
19 .. autoclass:: cubicweb.web.views.forms.EntityFieldsForm |
|
20 |
|
21 As you have probably guessed, choosing between them is easy. Simply ask you the |
|
22 question 'I am editing an entity or not?'. If the answer is yes, use |
|
23 :class:`EntityFieldsForm`, else use :class:`FieldsForm`. |
|
24 |
|
25 Actually there exists a third form class: |
|
26 |
|
27 .. autoclass:: cubicweb.web.views.forms.CompositeForm |
|
28 |
|
29 but you'll use this one rarely. |
7 """ |
30 """ |
8 __docformat__ = "restructuredtext en" |
31 __docformat__ = "restructuredtext en" |
9 |
32 |
10 from warnings import warn |
33 from warnings import warn |
11 |
34 |
14 from logilab.common.deprecation import deprecated |
37 from logilab.common.deprecation import deprecated |
15 |
38 |
16 from cubicweb import typed_eid |
39 from cubicweb import typed_eid |
17 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
40 from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset |
18 from cubicweb.web import uicfg, form, formwidgets as fwdgs |
41 from cubicweb.web import uicfg, form, formwidgets as fwdgs |
19 from cubicweb.web.formfields import StringField, relvoc_unrelated, guess_field |
42 from cubicweb.web.formfields import relvoc_unrelated, guess_field |
20 |
43 |
21 |
44 |
22 class FieldsForm(form.Form): |
45 class FieldsForm(form.Form): |
23 """base class for fields based forms. |
46 """This is the base class for fields based forms. |
|
47 |
|
48 **Attributes** |
24 |
49 |
25 The following attributes may be either set on subclasses or given on |
50 The following attributes may be either set on subclasses or given on |
26 form selection to customize the generated form: |
51 form selection to customize the generated form: |
27 |
52 |
28 * `needs_js`: sequence of javascript files that should be added to handle |
53 :attr:`needs_js` |
29 this form (through `req.add_js`) |
54 sequence of javascript files that should be added to handle this form |
30 |
55 (through :meth:`~cubicweb.web.request.Request.add_js`) |
31 * `needs_css`: sequence of css files that should be added to handle this |
56 |
32 form (through `req.add_css`) |
57 :attr:`needs_css` |
33 |
58 sequence of css files that should be added to handle this form (through |
34 * `domid`: value for the "id" attribute of the <form> tag |
59 :meth:`~cubicweb.web.request.Request.add_css`) |
35 |
60 |
36 * `action`: value for the "action" attribute of the <form> tag |
61 :attr:`domid` |
37 |
62 value for the "id" attribute of the <form> tag |
38 * `onsubmit`: value for the "onsubmit" attribute of the <form> tag |
63 |
39 |
64 :attr:`action` |
40 * `cssclass`: value for the "class" attribute of the <form> tag |
65 value for the "action" attribute of the <form> tag |
41 |
66 |
42 * `cssstyle`: value for the "style" attribute of the <form> tag |
67 :attr:`onsubmit` |
43 |
68 value for the "onsubmit" attribute of the <form> tag |
44 * `cwtarget`: value for the "cubicweb:target" attribute of the <form> tag |
69 |
45 |
70 :attr:`cssclass` |
46 * `redirect_path`: relative to redirect to after submitting the form |
71 value for the "class" attribute of the <form> tag |
47 |
72 |
48 * `copy_nav_params`: flag telling if navigation paramenters should be copied |
73 :attr:`cssstyle` |
49 back in hidden input |
74 value for the "style" attribute of the <form> tag |
50 |
75 |
51 * `form_buttons`: form buttons sequence (button widgets instances) |
76 :attr:`cwtarget` |
52 |
77 value for the "cubicweb:target" attribute of the <form> tag |
53 * `form_renderer_id`: id of the form renderer to use to render the form |
78 |
54 |
79 :attr:`redirect_path` |
55 * `fieldsets_in_order`: fieldset name sequence, to control order |
80 relative to redirect to after submitting the form |
|
81 |
|
82 :attr:`copy_nav_params` |
|
83 flag telling if navigation parameters should be copied back in hidden |
|
84 inputs |
|
85 |
|
86 :attr:`form_buttons` |
|
87 sequence of form control (:class:`~cubicweb.web.formwidgets.Button` |
|
88 widgets instances) |
|
89 |
|
90 :attr:`form_renderer_id` |
|
91 identifier of the form renderer to use to render the form |
|
92 |
|
93 :attr:`fieldsets_in_order` |
|
94 sequence of fieldset names , to control order |
|
95 |
|
96 **Generic methods** |
|
97 |
|
98 .. automethod:: cubicweb.web.form.Form.field_by_name(name, role=None) |
|
99 .. automethod:: cubicweb.web.form.Form.fields_by_name(name, role=None) |
|
100 |
|
101 **Form construction methods** |
|
102 |
|
103 .. automethod:: cubicweb.web.form.Form.remove_field(field) |
|
104 .. automethod:: cubicweb.web.form.Form.append_field(field) |
|
105 .. automethod:: cubicweb.web.form.Form.insert_field_before(field, name, role=None) |
|
106 .. automethod:: cubicweb.web.form.Form.insert_field_after(field, name, role=None) |
|
107 .. automethod:: cubicweb.web.form.Form.add_hidden(name, value=None, **kwargs) |
|
108 |
|
109 **Form rendering methods** |
|
110 |
|
111 .. automethod:: cubicweb.web.views.forms.FieldsForm.render |
|
112 |
56 """ |
113 """ |
57 __regid__ = 'base' |
114 __regid__ = 'base' |
58 |
115 |
59 |
116 |
60 # attributes overrideable by subclasses or through __init__ |
117 # attributes overrideable by subclasses or through __init__ |
73 @property |
130 @property |
74 def needs_multipart(self): |
131 def needs_multipart(self): |
75 """true if the form needs enctype=multipart/form-data""" |
132 """true if the form needs enctype=multipart/form-data""" |
76 return any(field.needs_multipart for field in self.fields) |
133 return any(field.needs_multipart for field in self.fields) |
77 |
134 |
78 def add_hidden(self, name, value=None, **kwargs): |
|
79 """add an hidden field to the form""" |
|
80 kwargs.setdefault('ignore_req_params', True) |
|
81 kwargs.setdefault('widget', fwdgs.HiddenInput) |
|
82 field = StringField(name=name, value=value, **kwargs) |
|
83 if 'id' in kwargs: |
|
84 # by default, hidden input don't set id attribute. If one is |
|
85 # explicitly specified, ensure it will be set |
|
86 field.widget.setdomid = True |
|
87 self.append_field(field) |
|
88 return field |
|
89 |
|
90 def add_media(self): |
135 def add_media(self): |
91 """adds media (CSS & JS) required by this widget""" |
136 """adds media (CSS & JS) required by this widget""" |
92 if self.needs_js: |
137 if self.needs_js: |
93 self._cw.add_js(self.needs_js) |
138 self._cw.add_js(self.needs_js) |
94 if self.needs_css: |
139 if self.needs_css: |
95 self._cw.add_css(self.needs_css) |
140 self._cw.add_css(self.needs_css) |
96 |
141 |
97 def render(self, formvalues=None, rendervalues=None, renderer=None, **kwargs): |
142 def render(self, formvalues=None, rendervalues=None, renderer=None, **kwargs): |
98 """render this form, using the renderer given in args or the default |
143 """Render this form, using the `renderer` given as argument or the |
99 FormRenderer() |
144 default according to :attr:`form_renderer_id`. The rendered form is |
|
145 returned as an unicode string. |
|
146 |
|
147 `formvalues` is an optional dictionary containing values that will be |
|
148 considered as field's value. |
|
149 |
|
150 Extra keyword arguments will be given to renderer's :meth:`render` method. |
|
151 |
|
152 `rendervalues` is deprecated. |
100 """ |
153 """ |
101 if rendervalues is not None: |
154 if rendervalues is not None: |
102 warn('[3.6] rendervalues argument is deprecated, all named arguments will be given instead', |
155 warn('[3.6] rendervalues argument is deprecated, all named arguments will be given instead', |
103 DeprecationWarning, stacklevel=2) |
156 DeprecationWarning, stacklevel=2) |
104 kwargs = rendervalues |
157 kwargs = rendervalues |
146 |
199 |
147 _AFF = uicfg.autoform_field |
200 _AFF = uicfg.autoform_field |
148 _AFF_KWARGS = uicfg.autoform_field_kwargs |
201 _AFF_KWARGS = uicfg.autoform_field_kwargs |
149 |
202 |
150 class EntityFieldsForm(FieldsForm): |
203 class EntityFieldsForm(FieldsForm): |
|
204 """This class is designed for forms used to edit some entities. It should |
|
205 handle for you all the underlying stuff necessary to properly work with the |
|
206 generic :class:`~cubicweb.web.views.editcontroller.EditController`. |
|
207 """ |
|
208 |
151 __regid__ = 'base' |
209 __regid__ = 'base' |
152 __select__ = (match_kwargs('entity') |
210 __select__ = (match_kwargs('entity') |
153 | (one_line_rset() & non_final_entity())) |
211 | (one_line_rset() & non_final_entity())) |
154 |
212 |
155 internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid') |
213 internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid') |
265 def object_relation_vocabulary(self, rtype, limit=None): |
323 def object_relation_vocabulary(self, rtype, limit=None): |
266 return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None) |
324 return relvoc_unrelated(self.edited_entity, rtype, 'object', limit=None) |
267 |
325 |
268 |
326 |
269 class CompositeFormMixIn(object): |
327 class CompositeFormMixIn(object): |
270 """form composed of sub-forms""" |
|
271 __regid__ = 'composite' |
328 __regid__ = 'composite' |
272 form_renderer_id = __regid__ |
329 form_renderer_id = __regid__ |
273 |
330 |
274 def __init__(self, *args, **kwargs): |
331 def __init__(self, *args, **kwargs): |
275 super(CompositeFormMixIn, self).__init__(*args, **kwargs) |
332 super(CompositeFormMixIn, self).__init__(*args, **kwargs) |