61 need_multipart = False |
61 need_multipart = False |
62 # generate the "id" attribute with the same value as the "name" (html) attribute |
62 # generate the "id" attribute with the same value as the "name" (html) attribute |
63 autoid = True |
63 autoid = True |
64 html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress')) |
64 html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress')) |
65 cubicwebns_attributes = set() |
65 cubicwebns_attributes = set() |
66 |
66 |
67 def __init__(self, vreg, subjschema, rschema, objschema, |
67 def __init__(self, vreg, subjschema, rschema, objschema, |
68 role='subject', description=None, |
68 role='subject', description=None, |
69 **kwattrs): |
69 **kwattrs): |
70 self.vreg = vreg |
70 self.vreg = vreg |
71 self.rschema = rschema |
71 self.rschema = rschema |
81 def copy(self): |
81 def copy(self): |
82 """shallow copy (useful when you need to modify self.attrs |
82 """shallow copy (useful when you need to modify self.attrs |
83 because widget instances are cached) |
83 because widget instances are cached) |
84 """ |
84 """ |
85 # brute force copy (subclasses don't have the |
85 # brute force copy (subclasses don't have the |
86 # same __init__ prototype) |
86 # same __init__ prototype) |
87 widget = self.__new__(self.__class__) |
87 widget = self.__new__(self.__class__) |
88 widget.__dict__ = dict(self.__dict__) |
88 widget.__dict__ = dict(self.__dict__) |
89 widget.attrs = dict(widget.attrs) |
89 widget.attrs = dict(widget.attrs) |
90 return widget |
90 return widget |
91 |
91 |
92 @staticmethod |
92 @staticmethod |
93 def size_constraint_attrs(attrs, maxsize): |
93 def size_constraint_attrs(attrs, maxsize): |
94 """set html attributes in the attrs dict to consider maxsize""" |
94 """set html attributes in the attrs dict to consider maxsize""" |
95 pass |
95 pass |
96 |
96 |
103 if name in self.cubicwebns_attributes: |
103 if name in self.cubicwebns_attributes: |
104 attrs.append(u'cubicweb:%s="%s"' % (name, value)) |
104 attrs.append(u'cubicweb:%s="%s"' % (name, value)) |
105 elif name in self.html_attributes: |
105 elif name in self.html_attributes: |
106 attrs.append(u'%s="%s"' % (name, value)) |
106 attrs.append(u'%s="%s"' % (name, value)) |
107 return u' '.join(sorted(attrs)) |
107 return u' '.join(sorted(attrs)) |
108 |
108 |
109 def required(self, entity): |
109 def required(self, entity): |
110 """indicates if the widget needs a value to be filled in""" |
110 """indicates if the widget needs a value to be filled in""" |
111 card = self.rschema.cardinality(self.subjtype, self.objtype, self.role) |
111 card = self.rschema.cardinality(self.subjtype, self.objtype, self.role) |
112 return card in '1+' |
112 return card in '1+' |
113 |
113 |
114 def input_id(self, entity): |
114 def input_id(self, entity): |
115 try: |
115 try: |
116 return self.rname |
116 return self.rname |
117 except AttributeError: |
117 except AttributeError: |
118 return eid_param(self.name, entity.eid) |
118 return eid_param(self.name, entity.eid) |
119 |
119 |
120 def render_label(self, entity, label=None): |
120 def render_label(self, entity, label=None): |
121 """render widget's label""" |
121 """render widget's label""" |
122 label = label or self.rschema.display_name(entity.req, self.role) |
122 label = label or self.rschema.display_name(entity.req, self.role) |
123 forid = self.input_id(entity) |
123 forid = self.input_id(entity) |
124 if forid: |
124 if forid: |
128 if self.required(entity): |
128 if self.required(entity): |
129 label = u'<label class="required"%s>%s</label>' % (forattr, label) |
129 label = u'<label class="required"%s>%s</label>' % (forattr, label) |
130 else: |
130 else: |
131 label = u'<label%s>%s</label>' % (forattr, label) |
131 label = u'<label%s>%s</label>' % (forattr, label) |
132 return label |
132 return label |
133 |
133 |
134 def render_error(self, entity): |
134 def render_error(self, entity): |
135 """return validation error for widget's field of the given entity, if |
135 """return validation error for widget's field of the given entity, if |
136 any |
136 any |
137 """ |
137 """ |
138 errex = entity.req.data.get('formerrors') |
138 errex = entity.req.data.get('formerrors') |
151 example = self.render_example(req) |
151 example = self.render_example(req) |
152 if example: |
152 if example: |
153 help.append(u'<span class="helper">(%s: %s)</span>' |
153 help.append(u'<span class="helper">(%s: %s)</span>' |
154 % (req._('sample format'), example)) |
154 % (req._('sample format'), example)) |
155 return u' '.join(help) |
155 return u' '.join(help) |
156 |
156 |
157 def render_example(self, req): |
157 def render_example(self, req): |
158 return u'' |
158 return u'' |
159 |
159 |
160 def render(self, entity): |
160 def render(self, entity): |
161 """render the widget for a simple view""" |
161 """render the widget for a simple view""" |
162 if not entity.has_eid(): |
162 if not entity.has_eid(): |
163 return u'' |
163 return u'' |
164 return entity.printable_value(self.name) |
164 return entity.printable_value(self.name) |
165 |
165 |
166 def edit_render(self, entity, tabindex=None, |
166 def edit_render(self, entity, tabindex=None, |
167 includehelp=False, useid=None, **kwargs): |
167 includehelp=False, useid=None, **kwargs): |
168 """render the widget for edition""" |
168 """render the widget for edition""" |
169 # this is necessary to handle multiple edition |
169 # this is necessary to handle multiple edition |
170 self.rname = eid_param(self.name, entity.eid) |
170 self.rname = eid_param(self.name, entity.eid) |
178 self.attrs['tabindex'] = entity.req.next_tabindex() |
178 self.attrs['tabindex'] = entity.req.next_tabindex() |
179 output = self._edit_render(entity, **kwargs) |
179 output = self._edit_render(entity, **kwargs) |
180 if includehelp: |
180 if includehelp: |
181 output += self.render_help(entity) |
181 output += self.render_help(entity) |
182 return output |
182 return output |
183 |
183 |
184 def _edit_render(self, entity): |
184 def _edit_render(self, entity): |
185 """do the actual job to render the widget for edition""" |
185 """do the actual job to render the widget for edition""" |
186 raise NotImplementedError |
186 raise NotImplementedError |
187 |
187 |
188 def current_values(self, entity): |
188 def current_values(self, entity): |
194 if self.rschema.is_final(): |
194 if self.rschema.is_final(): |
195 return entity.attribute_values(self.name) |
195 return entity.attribute_values(self.name) |
196 elif entity.has_eid(): |
196 elif entity.has_eid(): |
197 return [row[0] for row in entity.related(self.name, self.role)] |
197 return [row[0] for row in entity.related(self.name, self.role)] |
198 return () |
198 return () |
199 |
199 |
200 def current_value(self, entity): |
200 def current_value(self, entity): |
201 return _value_from_values(self.current_values(entity)) |
201 return _value_from_values(self.current_values(entity)) |
202 |
202 |
203 def current_display_values(self, entity): |
203 def current_display_values(self, entity): |
204 """same as .current_values but consider values stored in session in case |
204 """same as .current_values but consider values stored in session in case |
211 if cdvalues is None: |
211 if cdvalues is None: |
212 return self.current_values(entity) |
212 return self.current_values(entity) |
213 if not isinstance(cdvalues, (list, tuple)): |
213 if not isinstance(cdvalues, (list, tuple)): |
214 cdvalues = (cdvalues,) |
214 cdvalues = (cdvalues,) |
215 return cdvalues |
215 return cdvalues |
216 |
216 |
217 def current_display_value(self, entity): |
217 def current_display_value(self, entity): |
218 """same as .current_value but consider values stored in session in case |
218 """same as .current_value but consider values stored in session in case |
219 of validation error |
219 of validation error |
220 """ |
220 """ |
221 return _value_from_values(self.current_display_values(entity)) |
221 return _value_from_values(self.current_display_values(entity)) |
222 |
222 |
223 def hidden_input(self, entity, qvalue): |
223 def hidden_input(self, entity, qvalue): |
224 """return an hidden field which |
224 """return an hidden field which |
225 1. indicates that a field is edited |
225 1. indicates that a field is edited |
226 2. hold the old value to easily detect if the field has been modified |
226 2. hold the old value to easily detect if the field has been modified |
227 |
227 |
256 input_type = 'hidden' |
256 input_type = 'hidden' |
257 autoid = False |
257 autoid = False |
258 def __init__(self, vreg, subjschema, rschema, objschema, |
258 def __init__(self, vreg, subjschema, rschema, objschema, |
259 role='subject', **kwattrs): |
259 role='subject', **kwattrs): |
260 InputWidget.__init__(self, vreg, subjschema, rschema, objschema, |
260 InputWidget.__init__(self, vreg, subjschema, rschema, objschema, |
261 role='subject', |
261 role='subject', |
262 **kwattrs) |
262 **kwattrs) |
263 # disable access key |
263 # disable access key |
264 del self.attrs['accesskey'] |
264 del self.attrs['accesskey'] |
265 |
265 |
266 def current_value(self, entity): |
266 def current_value(self, entity): |
268 return value or INTERNAL_FIELD_VALUE |
268 return value or INTERNAL_FIELD_VALUE |
269 |
269 |
270 def current_display_value(self, entity): |
270 def current_display_value(self, entity): |
271 value = InputWidget.current_display_value(self, entity) |
271 value = InputWidget.current_display_value(self, entity) |
272 return value or INTERNAL_FIELD_VALUE |
272 return value or INTERNAL_FIELD_VALUE |
273 |
273 |
274 def render_label(self, entity, label=None): |
274 def render_label(self, entity, label=None): |
275 """render widget's label""" |
275 """render widget's label""" |
276 return u'' |
276 return u'' |
277 |
277 |
278 def render_help(self, entity): |
278 def render_help(self, entity): |
279 return u'' |
279 return u'' |
280 |
280 |
281 def hidden_input(self, entity, value): |
281 def hidden_input(self, entity, value): |
282 """no hidden input for hidden input""" |
282 """no hidden input for hidden input""" |
283 return '' |
283 return '' |
284 |
284 |
285 |
285 |
286 class EidWidget(HiddenWidget): |
286 class EidWidget(HiddenWidget): |
287 |
287 |
288 def _edit_render(self, entity): |
288 def _edit_render(self, entity): |
289 return u'<input type="hidden" name="eid" value="%s" />' % entity.eid |
289 return u'<input type="hidden" name="eid" value="%s" />' % entity.eid |
295 @staticmethod |
295 @staticmethod |
296 def size_constraint_attrs(attrs, maxsize): |
296 def size_constraint_attrs(attrs, maxsize): |
297 """set html attributes in the attrs dict to consider maxsize""" |
297 """set html attributes in the attrs dict to consider maxsize""" |
298 attrs['size'] = min(maxsize, 40) |
298 attrs['size'] = min(maxsize, 40) |
299 attrs['maxlength'] = maxsize |
299 attrs['maxlength'] = maxsize |
300 |
300 |
301 |
301 |
302 class AutoCompletionWidget(StringWidget): |
302 class AutoCompletionWidget(StringWidget): |
303 cubicwebns_attributes = (StringWidget.cubicwebns_attributes | |
303 cubicwebns_attributes = (StringWidget.cubicwebns_attributes | |
304 set(('accesskey', 'size', 'maxlength'))) |
304 set(('accesskey', 'size', 'maxlength'))) |
305 attrs = () |
305 attrs = () |
306 |
306 |
307 wdgtype = 'SuggestField' |
307 wdgtype = 'SuggestField' |
308 |
308 |
309 def current_value(self, entity): |
309 def current_value(self, entity): |
310 value = StringWidget.current_value(self, entity) |
310 value = StringWidget.current_value(self, entity) |
311 return value or INTERNAL_FIELD_VALUE |
311 return value or INTERNAL_FIELD_VALUE |
312 |
312 |
313 def _get_url(self, entity): |
313 def _get_url(self, entity): |
342 'required' : cssclass, |
342 'required' : cssclass, |
343 }) |
343 }) |
344 |
344 |
345 class StaticFileAutoCompletionWidget(AutoCompletionWidget): |
345 class StaticFileAutoCompletionWidget(AutoCompletionWidget): |
346 wdgtype = 'StaticFileSuggestField' |
346 wdgtype = 'StaticFileSuggestField' |
347 |
347 |
348 def _get_url(self, entity): |
348 def _get_url(self, entity): |
349 return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema] |
349 return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema] |
350 |
350 |
351 class RestrictedAutoCompletionWidget(AutoCompletionWidget): |
351 class RestrictedAutoCompletionWidget(AutoCompletionWidget): |
352 wdgtype = 'RestrictedSuggestField' |
352 wdgtype = 'RestrictedSuggestField' |
353 |
353 |
354 |
354 |
355 class PasswordWidget(InputWidget): |
355 class PasswordWidget(InputWidget): |
356 input_type = 'password' |
356 input_type = 'password' |
357 |
357 |
358 def required(self, entity): |
358 def required(self, entity): |
359 if InputWidget.required(self, entity) and not entity.has_eid(): |
359 if InputWidget.required(self, entity) and not entity.has_eid(): |
360 return True |
360 return True |
361 return False |
361 return False |
362 |
362 |
363 def current_values(self, entity): |
363 def current_values(self, entity): |
364 # on existant entity, show password field has non empty (we don't have |
364 # on existant entity, show password field has non empty (we don't have |
365 # the actual value |
365 # the actual value |
366 if entity.has_eid(): |
366 if entity.has_eid(): |
367 return (INTERNAL_FIELD_VALUE,) |
367 return (INTERNAL_FIELD_VALUE,) |
372 name = eid_param(self.name + '-confirm', entity.eid) |
372 name = eid_param(self.name + '-confirm', entity.eid) |
373 return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/> <span class="emphasis">(%s)</span>' % ( |
373 return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/> <span class="emphasis">(%s)</span>' % ( |
374 html, self.input_type, name, name, entity.req.next_tabindex(), |
374 html, self.input_type, name, name, entity.req.next_tabindex(), |
375 entity.req._('confirm password')) |
375 entity.req._('confirm password')) |
376 |
376 |
377 |
377 |
378 class TextWidget(Widget): |
378 class TextWidget(Widget): |
379 html_attributes = Widget.html_attributes | set(('rows', 'cols')) |
379 html_attributes = Widget.html_attributes | set(('rows', 'cols')) |
380 |
380 |
381 @staticmethod |
381 @staticmethod |
382 def size_constraint_attrs(attrs, maxsize): |
382 def size_constraint_attrs(attrs, maxsize): |
383 """set html attributes in the attrs dict to consider maxsize""" |
383 """set html attributes in the attrs dict to consider maxsize""" |
384 if 256 < maxsize < 513: |
384 if 256 < maxsize < 513: |
385 attrs['cols'], attrs['rows'] = 60, 5 |
385 attrs['cols'], attrs['rows'] = 60, 5 |
386 else: |
386 else: |
387 attrs['cols'], attrs['rows'] = 80, 10 |
387 attrs['cols'], attrs['rows'] = 80, 10 |
388 |
388 |
389 def render(self, entity): |
389 def render(self, entity): |
390 if not entity.has_eid(): |
390 if not entity.has_eid(): |
391 return u'' |
391 return u'' |
392 return entity.printable_value(self.name) |
392 return entity.printable_value(self.name) |
393 |
393 |
394 def _edit_render(self, entity, with_format=True): |
394 def _edit_render(self, entity, with_format=True): |
395 req = entity.req |
395 req = entity.req |
396 editor = self._edit_render_textarea(entity, with_format) |
396 editor = self._edit_render_textarea(entity, with_format) |
397 value = self.current_value(entity) |
397 value = self.current_value(entity) |
398 if isinstance(value, basestring): |
398 if isinstance(value, basestring): |
399 value = html_escape(value) |
399 value = html_escape(value) |
400 return u'%s%s' % (self.hidden_input(entity, value), editor) |
400 return u'%s%s' % (self.hidden_input(entity, value), editor) |
401 |
401 |
402 def _edit_render_textarea(self, entity, with_format): |
402 def _edit_render_textarea(self, entity, with_format): |
403 self.attrs.setdefault('cols', 80) |
403 self.attrs.setdefault('cols', 80) |
404 self.attrs.setdefault('rows', 20) |
404 self.attrs.setdefault('rows', 20) |
405 dvalue = self.current_display_value(entity) |
405 dvalue = self.current_display_value(entity) |
406 if isinstance(dvalue, basestring): |
406 if isinstance(dvalue, basestring): |
424 self.attrs['tabindex'] = entity.req.next_tabindex() |
424 self.attrs['tabindex'] = entity.req.next_tabindex() |
425 else: |
425 else: |
426 fmtwdgstr = '' |
426 fmtwdgstr = '' |
427 return u'%s<br/><textarea onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % ( |
427 return u'%s<br/><textarea onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % ( |
428 fmtwdgstr, self.rname, self.format_attrs(), dvalue) |
428 fmtwdgstr, self.rname, self.format_attrs(), dvalue) |
429 |
429 |
430 |
430 |
431 class CheckBoxWidget(Widget): |
431 class CheckBoxWidget(Widget): |
432 html_attributes = Widget.html_attributes | set(('checked', )) |
432 html_attributes = Widget.html_attributes | set(('checked', )) |
433 def _edit_render(self, entity): |
433 def _edit_render(self, entity): |
434 value = self.current_value(entity) |
434 value = self.current_value(entity) |
435 dvalue = self.current_display_value(entity) |
435 dvalue = self.current_display_value(entity) |
458 wdgs = [self.hidden_input(entity, value), |
458 wdgs = [self.hidden_input(entity, value), |
459 u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')), |
459 u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')), |
460 u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))] |
460 u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))] |
461 return '\n'.join(wdgs) |
461 return '\n'.join(wdgs) |
462 |
462 |
463 |
463 |
464 class FileWidget(Widget): |
464 class FileWidget(Widget): |
465 need_multipart = True |
465 need_multipart = True |
466 def _file_wdg(self, entity): |
466 def _file_wdg(self, entity): |
467 wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())] |
467 wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())] |
468 req = entity.req |
468 req = entity.req |
490 wdgs.append(req._('detach attached file %s' % entity.dc_title())) |
490 wdgs.append(req._('detach attached file %s' % entity.dc_title())) |
491 else: |
491 else: |
492 wdgs.append(u'<br/>') |
492 wdgs.append(u'<br/>') |
493 wdgs.append(req._('currently attached file: %s' % entity.dc_title())) |
493 wdgs.append(req._('currently attached file: %s' % entity.dc_title())) |
494 return '\n'.join(wdgs) |
494 return '\n'.join(wdgs) |
495 |
495 |
496 def _edit_render(self, entity): |
496 def _edit_render(self, entity): |
497 return self.hidden_input(entity, None) + self._file_wdg(entity) |
497 return self.hidden_input(entity, None) + self._file_wdg(entity) |
498 |
498 |
499 |
499 |
500 class TextFileWidget(FileWidget): |
500 class TextFileWidget(FileWidget): |
508 else: |
508 else: |
509 msg = entity.req._( |
509 msg = entity.req._( |
510 'You can either submit a new file using the browse button above' |
510 'You can either submit a new file using the browse button above' |
511 ', or edit file content online with the widget below.') |
511 ', or edit file content online with the widget below.') |
512 return msg |
512 return msg |
513 |
513 |
514 def _edit_render(self, entity): |
514 def _edit_render(self, entity): |
515 wdgs = [self._file_wdg(entity)] |
515 wdgs = [self._file_wdg(entity)] |
516 if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'): |
516 if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'): |
517 msg = self._edit_msg(entity) |
517 msg = self._edit_msg(entity) |
518 wdgs.append(u'<p><b>%s</b></p>' % msg) |
518 wdgs.append(u'<p><b>%s</b></p>' % msg) |
532 return '\n'.join(wdgs) |
532 return '\n'.join(wdgs) |
533 |
533 |
534 |
534 |
535 class ComboBoxWidget(Widget): |
535 class ComboBoxWidget(Widget): |
536 html_attributes = Widget.html_attributes | set(('multiple', 'size')) |
536 html_attributes = Widget.html_attributes | set(('multiple', 'size')) |
537 |
537 |
538 def __init__(self, vreg, subjschema, rschema, objschema, |
538 def __init__(self, vreg, subjschema, rschema, objschema, |
539 multiple=False, **kwattrs): |
539 multiple=False, **kwattrs): |
540 super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, |
540 super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, |
541 **kwattrs) |
541 **kwattrs) |
542 if multiple: |
542 if multiple: |
543 self.attrs['multiple'] = 'multiple' |
543 self.attrs['multiple'] = 'multiple' |
544 if not 'size' in self.attrs: |
544 if not 'size' in self.attrs: |
545 self.attrs['size'] = '5' |
545 self.attrs['size'] = '5' |
546 # disable access key (dunno why but this is not allowed by xhtml 1.0) |
546 # disable access key (dunno why but this is not allowed by xhtml 1.0) |
547 del self.attrs['accesskey'] |
547 del self.attrs['accesskey'] |
548 |
548 |
549 def vocabulary(self, entity): |
549 def vocabulary(self, entity): |
550 raise NotImplementedError() |
550 raise NotImplementedError() |
551 |
551 |
552 def form_value(self, entity, value, values): |
552 def form_value(self, entity, value, values): |
553 if value in values: |
553 if value in values: |
554 flag = 'selected="selected"' |
554 flag = 'selected="selected"' |
555 else: |
555 else: |
556 flag = '' |
556 flag = '' |
572 value, flag = self.form_value(entity, value, dvalues) |
572 value, flag = self.form_value(entity, value, dvalues) |
573 res.append(u'<option value="%s" %s>%s</option>' % (value, flag, html_escape(label))) |
573 res.append(u'<option value="%s" %s>%s</option>' % (value, flag, html_escape(label))) |
574 res.append(u'</select>') |
574 res.append(u'</select>') |
575 return '\n'.join(res) |
575 return '\n'.join(res) |
576 |
576 |
577 |
577 |
578 class StaticComboBoxWidget(ComboBoxWidget): |
578 class StaticComboBoxWidget(ComboBoxWidget): |
579 |
579 |
580 def __init__(self, vreg, subjschema, rschema, objschema, |
580 def __init__(self, vreg, subjschema, rschema, objschema, |
581 vocabfunc, multiple=False, sort=False, **kwattrs): |
581 vocabfunc, multiple=False, sort=False, **kwattrs): |
582 super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, |
582 super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, |
583 multiple, **kwattrs) |
583 multiple, **kwattrs) |
584 self.sort = sort |
584 self.sort = sort |
589 if self.sort: |
589 if self.sort: |
590 choices = sorted(choices) |
590 choices = sorted(choices) |
591 if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'): |
591 if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'): |
592 return zip((entity.req._(v) for v in choices), choices) |
592 return zip((entity.req._(v) for v in choices), choices) |
593 return zip(choices, choices) |
593 return zip(choices, choices) |
594 |
594 |
595 |
595 |
596 class EntityLinkComboBoxWidget(ComboBoxWidget): |
596 class EntityLinkComboBoxWidget(ComboBoxWidget): |
597 """to be used be specific forms""" |
597 """to be used be specific forms""" |
598 |
598 |
599 def current_values(self, entity): |
599 def current_values(self, entity): |
600 if entity.has_eid(): |
600 if entity.has_eid(): |
601 return [r[0] for r in entity.related(self.name, self.role)] |
601 return [r[0] for r in entity.related(self.name, self.role)] |
602 defaultmeth = 'default_%s_%s' % (self.role, self.name) |
602 defaultmeth = 'default_%s_%s' % (self.role, self.name) |
603 if hasattr(entity, defaultmeth): |
603 if hasattr(entity, defaultmeth): |
604 return getattr(entity, defaultmeth)() |
604 return getattr(entity, defaultmeth)() |
605 return () |
605 return () |
606 |
606 |
607 def vocabulary(self, entity): |
607 def vocabulary(self, entity): |
608 return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role) |
608 return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role) |
609 |
609 |
610 |
610 |
611 class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget): |
611 class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget): |
612 |
612 |
613 def vocabulary(self, entity, limit=None): |
613 def vocabulary(self, entity, limit=None): |
614 req = entity.req |
614 req = entity.req |
615 # first see if its specified by __linkto form parameters |
615 # first see if its specified by __linkto form parameters |
616 linkedto = entity.linked_to(self.name, self.role) |
616 linkedto = entity.linked_to(self.name, self.role) |
617 if linkedto: |
617 if linkedto: |
630 relatedvocab = [] |
630 relatedvocab = [] |
631 return res + entity.vocabulary(self.rschema, self.role) + relatedvocab |
631 return res + entity.vocabulary(self.rschema, self.role) + relatedvocab |
632 |
632 |
633 |
633 |
634 class DynamicComboBoxWidget(RawDynamicComboBoxWidget): |
634 class DynamicComboBoxWidget(RawDynamicComboBoxWidget): |
635 |
635 |
636 def vocabulary(self, entity, limit=None): |
636 def vocabulary(self, entity, limit=None): |
637 return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit)) |
637 return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit)) |
638 |
638 |
639 |
639 |
640 class AddComboBoxWidget(DynamicComboBoxWidget): |
640 class AddComboBoxWidget(DynamicComboBoxWidget): |
667 class IntegerWidget(StringWidget): |
667 class IntegerWidget(StringWidget): |
668 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
668 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
669 kwattrs['size'] = 5 |
669 kwattrs['size'] = 5 |
670 kwattrs['maxlength'] = 15 |
670 kwattrs['maxlength'] = 15 |
671 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
671 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
672 |
672 |
673 def render_example(self, req): |
673 def render_example(self, req): |
674 return '23' |
674 return '23' |
675 |
675 |
676 |
676 |
677 class FloatWidget(StringWidget): |
677 class FloatWidget(StringWidget): |
678 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
678 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
679 kwattrs['size'] = 5 |
679 kwattrs['size'] = 5 |
680 kwattrs['maxlength'] = 15 |
680 kwattrs['maxlength'] = 15 |
681 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
681 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
682 |
682 |
683 def render_example(self, req): |
683 def render_example(self, req): |
684 formatstr = req.property_value('ui.float-format') |
684 formatstr = req.property_value('ui.float-format') |
685 return formatstr % 1.23 |
685 return formatstr % 1.23 |
686 |
686 |
687 def current_values(self, entity): |
687 def current_values(self, entity): |
688 values = entity.attribute_values(self.name) |
688 values = entity.attribute_values(self.name) |
689 if values: |
689 if values: |
690 formatstr = entity.req.property_value('ui.float-format') |
690 formatstr = entity.req.property_value('ui.float-format') |
691 value = values[0] |
691 value = values[0] |
700 class DecimalWidget(StringWidget): |
700 class DecimalWidget(StringWidget): |
701 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
701 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
702 kwattrs['size'] = 5 |
702 kwattrs['size'] = 5 |
703 kwattrs['maxlength'] = 15 |
703 kwattrs['maxlength'] = 15 |
704 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
704 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
705 |
705 |
706 def render_example(self, req): |
706 def render_example(self, req): |
707 return '345.0300' |
707 return '345.0300' |
708 |
708 |
709 |
709 |
710 class DateWidget(StringWidget): |
710 class DateWidget(StringWidget): |
722 _ = req._ |
722 _ = req._ |
723 monthnames = [_(mname) for mname in cls.monthnames] |
723 monthnames = [_(mname) for mname in cls.monthnames] |
724 daynames = [_(dname) for dname in cls.daynames] |
724 daynames = [_(dname) for dname in cls.daynames] |
725 req.html_headers.define_var('MONTHNAMES', monthnames) |
725 req.html_headers.define_var('MONTHNAMES', monthnames) |
726 req.html_headers.define_var('DAYNAMES', daynames) |
726 req.html_headers.define_var('DAYNAMES', daynames) |
727 |
727 |
728 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
728 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
729 kwattrs.setdefault('size', 10) |
729 kwattrs.setdefault('size', 10) |
730 kwattrs.setdefault('maxlength', 10) |
730 kwattrs.setdefault('maxlength', 10) |
731 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
731 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
732 |
732 |
782 |
782 |
783 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
783 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
784 kwattrs['size'] = 16 |
784 kwattrs['size'] = 16 |
785 kwattrs['maxlength'] = 16 |
785 kwattrs['maxlength'] = 16 |
786 DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
786 DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
787 |
787 |
788 def render_example(self, req): |
788 def render_example(self, req): |
789 formatstr1 = req.property_value('ui.datetime-format') |
789 formatstr1 = req.property_value('ui.datetime-format') |
790 formatstr2 = req.property_value('ui.date-format') |
790 formatstr2 = req.property_value('ui.date-format') |
791 return req._('%(fmt1)s, or without time: %(fmt2)s') % { |
791 return req._('%(fmt1)s, or without time: %(fmt2)s') % { |
792 'fmt1': datetime.now().strftime(formatstr1), |
792 'fmt1': datetime.now().strftime(formatstr1), |
799 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
799 def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): |
800 kwattrs['size'] = 5 |
800 kwattrs['size'] = 5 |
801 kwattrs['maxlength'] = 5 |
801 kwattrs['maxlength'] = 5 |
802 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
802 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) |
803 |
803 |
804 |
804 |
805 class EmailWidget(StringWidget): |
805 class EmailWidget(StringWidget): |
806 |
806 |
807 def render(self, entity): |
807 def render(self, entity): |
808 email = getattr(entity, self.name) |
808 email = getattr(entity, self.name) |
809 if not email: |
809 if not email: |
810 return u'' |
810 return u'' |
811 return u'<a href="mailto:%s">%s</a>' % (email, email) |
811 return u'<a href="mailto:%s">%s</a>' % (email, email) |
812 |
812 |
813 class URLWidget(StringWidget): |
813 class URLWidget(StringWidget): |
814 |
814 |
815 def render(self, entity): |
815 def render(self, entity): |
816 url = getattr(entity, self.name) |
816 url = getattr(entity, self.name) |
817 if not url: |
817 if not url: |
818 return u'' |
818 return u'' |
819 url = html_escape(url) |
819 url = html_escape(url) |
820 return u'<a href="%s">%s</a>' % (url, url) |
820 return u'<a href="%s">%s</a>' % (url, url) |
821 |
821 |
822 class EmbededURLWidget(StringWidget): |
822 class EmbededURLWidget(StringWidget): |
823 |
823 |
824 def render(self, entity): |
824 def render(self, entity): |
825 url = getattr(entity, self.name) |
825 url = getattr(entity, self.name) |
826 if not url: |
826 if not url: |
827 return u'' |
827 return u'' |
828 aurl = html_escape(entity.build_url('embed', url=url)) |
828 aurl = html_escape(entity.build_url('embed', url=url)) |
829 return u'<a href="%s">%s</a>' % (aurl, url) |
829 return u'<a href="%s">%s</a>' % (aurl, url) |
830 |
830 |
831 |
831 |
832 |
832 |
833 def widget_factory(vreg, subjschema, rschema, objschema, role='subject', |
833 def widget_factory(vreg, subjschema, rschema, objschema, role='subject', |
834 **kwargs): |
834 **kwargs): |
835 """return the most adapated widget to edit the relation |
835 """return the most adapated widget to edit the relation |
836 'subjschema rschema objschema' according to information found in the schema |
836 'subjschema rschema objschema' according to information found in the schema |
855 return factory(vreg, subjschema, rschema, objschema, wcls=wcls, |
855 return factory(vreg, subjschema, rschema, objschema, wcls=wcls, |
856 role=role, **kwargs) |
856 role=role, **kwargs) |
857 |
857 |
858 |
858 |
859 # factories to find the most adapated widget according to a type and other constraints |
859 # factories to find the most adapated widget according to a type and other constraints |
860 |
860 |
861 def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs): |
861 def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs): |
862 w = None |
862 w = None |
863 for c in rschema.rproperty(subjschema, objschema, 'constraints'): |
863 for c in rschema.rproperty(subjschema, objschema, 'constraints'): |
864 if isinstance(c, StaticVocabularyConstraint): |
864 if isinstance(c, StaticVocabularyConstraint): |
865 # may have been set by a previous SizeConstraint but doesn't make sense |
865 # may have been set by a previous SizeConstraint but doesn't make sense |
866 # here (even doesn't have the same meaning on a combobox actually) |
866 # here (even doesn't have the same meaning on a combobox actually) |
867 kwargs.pop('size', None) |
867 kwargs.pop('size', None) |
868 return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema, |
868 return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema, |
869 vocabfunc=c.vocabulary, **kwargs) |
869 vocabfunc=c.vocabulary, **kwargs) |
870 if isinstance(c, SizeConstraint) and c.max is not None: |
870 if isinstance(c, SizeConstraint) and c.max is not None: |
871 # don't return here since a StaticVocabularyConstraint may |
871 # don't return here since a StaticVocabularyConstraint may |
872 # follow |
872 # follow |
912 'Decimal': DecimalWidget, |
912 'Decimal': DecimalWidget, |
913 'Password': PasswordWidget, |
913 'Password': PasswordWidget, |
914 'String' : StringWidget, |
914 'String' : StringWidget, |
915 'Time': TimeWidget, |
915 'Time': TimeWidget, |
916 } |
916 } |
917 |
917 |
918 # widgets registry |
918 # widgets registry |
919 WIDGETS = {} |
919 WIDGETS = {} |
920 def register(widget_list): |
920 def register(widget_list): |
921 for obj in widget_list: |
921 for obj in widget_list: |
922 if isinstance(obj, type) and issubclass(obj, Widget): |
922 if isinstance(obj, type) and issubclass(obj, Widget): |