# HG changeset patch # User Sylvain Thénault # Date 1243599570 -7200 # Node ID e8032965f37a7790ef52101e4b89f26ccf18c3a3 # Parent ea9eab290dcda8be636c60eedccb5308d9c861ca turn every form class into appobject. They should not be instantiated manually anymore. diff -r ea9eab290dcd -r e8032965f37a common/mixins.py --- a/common/mixins.py Fri May 29 14:07:42 2009 +0200 +++ b/common/mixins.py Fri May 29 14:19:30 2009 +0200 @@ -237,8 +237,8 @@ @obsolete('use EntityFieldsForm.subject_in_state_vocabulary') def subject_in_state_vocabulary(self, rschema, limit=None): - from cubicweb.web.form import EntityFieldsForm - return EntityFieldsForm(self.req, None, entity=self).subject_in_state_vocabulary(rschema, limit) + form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + return form.subject_in_state_vocabulary(rschema, limit) diff -r ea9eab290dcd -r e8032965f37a entities/__init__.py --- a/entities/__init__.py Fri May 29 14:07:42 2009 +0200 +++ b/entities/__init__.py Fri May 29 14:19:30 2009 +0200 @@ -242,13 +242,13 @@ @obsolete('use EntityFieldsForm.subject_relation_vocabulary') def subject_relation_vocabulary(self, rtype, limit): - from cubicweb.web.form import EntityFieldsForm - return EntityFieldsForm(self.req, None, entity=self).subject_relation_vocabulary(rtype, limit) + form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + return form.subject_relation_vocabulary(rtype, limit) @obsolete('use EntityFieldsForm.object_relation_vocabulary') def object_relation_vocabulary(self, rtype, limit): - from cubicweb.web.form import EntityFieldsForm - return EntityFieldsForm(self.req, None, entity=self).object_relation_vocabulary(rtype, limit) + form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + return form.object_relation_vocabulary(rtype, limit) @obsolete('use AutomaticEntityForm.[e]relations_by_category') def relations_by_category(self, categories=None, permission=None): diff -r ea9eab290dcd -r e8032965f37a entity.py --- a/entity.py Fri May 29 14:07:42 2009 +0200 +++ b/entity.py Fri May 29 14:19:30 2009 +0200 @@ -724,9 +724,8 @@ If `eid` is None in one of these couples, it should be interpreted as a separator in case vocabulary results are grouped """ - from cubicweb.web.form import EntityFieldsForm from logilab.common.testlib import mock_object - form = EntityFieldsForm(self.req, entity=self) + form = self.vreg.select_object('forms', 'edition', self.req, entity=self) field = mock_object(name=rtype, role=role) return form.form_field_vocabulary(field, limit) diff -r ea9eab290dcd -r e8032965f37a web/form.py --- a/web/form.py Fri May 29 14:07:42 2009 +0200 +++ b/web/form.py Fri May 29 14:19:30 2009 +0200 @@ -7,20 +7,10 @@ """ __docformat__ = "restructuredtext en" -from warnings import warn - -from logilab.common.compat import any -from logilab.common.decorators import iclassmethod - from cubicweb.appobject import AppRsetObject -from cubicweb.selectors import yes, non_final_entity, match_kwargs, one_line_rset from cubicweb.view import NOINDEX, NOFOLLOW from cubicweb.common import tags -from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs -from cubicweb.web import formwidgets as fwdgs, httpcache -from cubicweb.web.controller import NAV_FORM_PARAMETERS -from cubicweb.web.formfields import (Field, StringField, RelationField, - HiddenInitialValueField) +from cubicweb.web import stdmsgs, httpcache, formfields class FormViewMixIn(object): @@ -198,7 +188,7 @@ if hasattr(base, '_fields_'): allfields += base._fields_ clsfields = (item for item in classdict.items() - if isinstance(item[1], Field)) + if isinstance(item[1], formfields.Field)) for fieldname, field in sorted(clsfields, key=lambda x: x[1].creation_rank): if not field.name: field.set_name(fieldname) @@ -212,509 +202,6 @@ found """ -class FieldsForm(FormMixIn, AppRsetObject): +class Form(FormMixIn, AppRsetObject): __metaclass__ = metafieldsform __registry__ = 'forms' - __select__ = yes() - - is_subform = False - - # attributes overrideable through __init__ - internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS - needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',) - needs_css = ('cubicweb.form.css',) - domid = 'form' - title = None - action = None - onsubmit = "return freezeFormButtons('%(domid)s');" - cssclass = None - cssstyle = None - cwtarget = None - redirect_path = None - set_error_url = True - copy_nav_params = False - form_buttons = None # form buttons (button widgets instances) - form_renderer_id = 'default' - - def __init__(self, req, rset=None, row=None, col=None, submitmsg=None, - **kwargs): - super(FieldsForm, self).__init__(req, rset, row=row, col=col) - self.fields = list(self.__class__._fields_) - for key, val in kwargs.items(): - if key in NAV_FORM_PARAMETERS: - self.form_add_hidden(key, val) - else: - assert hasattr(self.__class__, key) and not key[0] == '_', key - setattr(self, key, val) - if self.set_error_url: - self.form_add_hidden('__errorurl', self.session_key()) - if self.copy_nav_params: - for param in NAV_FORM_PARAMETERS: - if not param in kwargs: - value = req.form.get(param) - if value: - self.form_add_hidden(param, value) - if submitmsg is not None: - self.form_add_hidden('__message', submitmsg) - self.context = None - if 'domid' in kwargs:# session key changed - self.restore_previous_post(self.session_key()) - - @iclassmethod - def _fieldsattr(cls_or_self): - if isinstance(cls_or_self, type): - fields = cls_or_self._fields_ - else: - fields = cls_or_self.fields - return fields - - @iclassmethod - def field_by_name(cls_or_self, name, role='subject'): - """return field with the given name and role. - Raise FieldNotFound if the field can't be found. - """ - for field in cls_or_self._fieldsattr(): - if field.name == name and field.role == role: - return field - raise FieldNotFound(name) - - @iclassmethod - def fields_by_name(cls_or_self, name, role='subject'): - """return a list of fields with the given name and role""" - return [field for field in cls_or_self._fieldsattr() - if field.name == name and field.role == role] - - @iclassmethod - def remove_field(cls_or_self, field): - """remove a field from form class or instance""" - cls_or_self._fieldsattr().remove(field) - - @iclassmethod - def append_field(cls_or_self, field): - """append a field to form class or instance""" - cls_or_self._fieldsattr().append(field) - - @iclassmethod - def insert_field_before(cls_or_self, new_field, name, role='subject'): - field = cls_or_self.field_by_name(name, role) - fields = cls_or_self._fieldsattr() - fields.insert(fields.index(field), new_field) - - @iclassmethod - def insert_field_after(cls_or_self, new_field, name, role='subject'): - field = cls_or_self.field_by_name(name, role) - fields = cls_or_self._fieldsattr() - fields.insert(fields.index(field)+1, new_field) - - @property - def form_needs_multipart(self): - """true if the form needs enctype=multipart/form-data""" - return any(field.needs_multipart for field in self.fields) - - def form_add_hidden(self, name, value=None, **kwargs): - """add an hidden field to the form""" - field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value, - **kwargs) - if 'id' in kwargs: - # by default, hidden input don't set id attribute. If one is - # explicitly specified, ensure it will be set - field.widget.setdomid = True - self.append_field(field) - return field - - def add_media(self): - """adds media (CSS & JS) required by this widget""" - if self.needs_js: - self.req.add_js(self.needs_js) - if self.needs_css: - self.req.add_css(self.needs_css) - - def form_render(self, **values): - """render this form, using the renderer given in args or the default - FormRenderer() - """ - renderer = values.pop('renderer', None) - if renderer is None: - renderer = self.form_default_renderer() - return renderer.render(self, values) - - def form_default_renderer(self): - return self.vreg.select_object('formrenderers', self.form_renderer_id, - self.req, self.rset, - row=self.row, col=self.col) - - def form_build_context(self, rendervalues=None): - """build form context values (the .context attribute which is a - dictionary with field instance as key associated to a dictionary - containing field 'name' (qualified), 'id', 'value' (for display, always - a string). - - rendervalues is an optional dictionary containing extra kwargs given to - form_render() - """ - self.context = context = {} - # ensure rendervalues is a dict - if rendervalues is None: - rendervalues = {} - # use a copy in case fields are modified while context is build (eg - # __linkto handling for instance) - for field in self.fields[:]: - for field in field.actual_fields(self): - field.form_init(self) - value = self.form_field_display_value(field, rendervalues) - context[field] = {'value': value, - 'name': self.form_field_name(field), - 'id': self.form_field_id(field), - } - - def form_field_display_value(self, field, rendervalues, load_bytes=False): - """return field's *string* value to use for display - - looks in - 1. previously submitted form values if any (eg on validation error) - 2. req.form - 3. extra kw args given to render_form - 4. field's typed value - - values found in 1. and 2. are expected te be already some 'display' - value while those found in 3. and 4. are expected to be correctly typed. - """ - value = self._req_display_value(field) - if value is None: - if field.name in rendervalues: - value = rendervalues[field.name] - else: - value = self.form_field_value(field, load_bytes) - if callable(value): - value = value(self) - if value != INTERNAL_FIELD_VALUE: - value = field.format_value(self.req, value) - return value - - def _req_display_value(self, field): - qname = self.form_field_name(field) - if qname in self.form_previous_values: - return self.form_previous_values[qname] - if qname in self.req.form: - return self.req.form[qname] - if field.name in self.req.form: - return self.req.form[field.name] - return None - - def form_field_value(self, field, load_bytes=False): - """return field's *typed* value""" - myattr = '%s_%s_default' % (field.role, field.name) - if hasattr(self, myattr): - return getattr(self, myattr)() - value = field.initial - if callable(value): - value = value(self) - return value - - def form_field_error(self, field): - """return validation error for widget's field, if any""" - if self._field_has_error(field): - self.form_displayed_errors.add(field.name) - return u'%s' % self.form_valerror.errors[field.name] - return u'' - - def form_field_format(self, field): - """return MIME type used for the given (text or bytes) field""" - return self.req.property_value('ui.default-text-format') - - def form_field_encoding(self, field): - """return encoding used for the given (text) field""" - return self.req.encoding - - def form_field_name(self, field): - """return qualified name for the given field""" - return field.name - - def form_field_id(self, field): - """return dom id for the given field""" - return field.id - - def form_field_vocabulary(self, field, limit=None): - """return vocabulary for the given field. Should be overriden in - specific forms using fields which requires some vocabulary - """ - raise NotImplementedError - - def _field_has_error(self, field): - """return true if the field has some error in given validation exception - """ - return self.form_valerror and field.name in self.form_valerror.errors - - -class EntityFieldsForm(FieldsForm): - __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity())) - - internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid') - domid = 'entityForm' - - def __init__(self, *args, **kwargs): - self.edited_entity = kwargs.pop('entity', None) - msg = kwargs.pop('submitmsg', None) - super(EntityFieldsForm, self).__init__(*args, **kwargs) - if self.edited_entity is None: - self.edited_entity = self.complete_entity(self.row or 0, self.col or 0) - self.form_add_hidden('__type', eidparam=True) - self.form_add_hidden('eid') - if msg: - # If we need to directly attach the new object to another one - self.form_add_hidden('__message', msg) - if not self.is_subform: - for linkto in self.req.list_form_param('__linkto'): - self.form_add_hidden('__linkto', linkto) - msg = '%s %s' % (msg, self.req._('and linked')) - # in case of direct instanciation - self.schema = self.edited_entity.schema - self.vreg = self.edited_entity.vreg - - def _field_has_error(self, field): - """return true if the field has some error in given validation exception - """ - return super(EntityFieldsForm, self)._field_has_error(field) \ - and self.form_valerror.eid == self.edited_entity.eid - - def _relation_vocabulary(self, rtype, targettype, role, - limit=None, done=None): - """return unrelated entities for a given relation and target entity type - for use in vocabulary - """ - if done is None: - done = set() - rset = self.edited_entity.unrelated(rtype, targettype, role, limit) - res = [] - for entity in rset.entities(): - if entity.eid in done: - continue - done.add(entity.eid) - res.append((entity.view('combobox'), entity.eid)) - return res - - def _req_display_value(self, field): - value = super(EntityFieldsForm, self)._req_display_value(field) - if value is None: - value = self.edited_entity.linked_to(field.name, field.role) - if value: - searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role) - for eid in value] - # remove associated __linkto hidden fields - for field in self.fields_by_name('__linkto'): - if field.initial in searchedvalues: - self.remove_field(field) - else: - value = None - return value - - def _form_field_default_value(self, field, load_bytes): - defaultattr = 'default_%s' % field.name - if hasattr(self.edited_entity, defaultattr): - # XXX bw compat, default_ on the entity - warn('found %s on %s, should be set on a specific form' - % (defaultattr, self.edited_entity.id), DeprecationWarning) - value = getattr(self.edited_entity, defaultattr) - if callable(value): - value = value() - else: - value = super(EntityFieldsForm, self).form_field_value(field, - load_bytes) - return value - - def form_default_renderer(self): - return self.vreg.select_object('formrenderers', self.form_renderer_id, - self.req, self.rset, - row=self.row, col=self.col, - entity=self.edited_entity) - - def form_build_context(self, values=None): - """overriden to add edit[s|o] hidden fields and to ensure schema fields - have eidparam set to True - - edit[s|o] hidden fields are used to indicate the value for the - associated field before the (potential) modification made when - submitting the form. - """ - eschema = self.edited_entity.e_schema - for field in self.fields[:]: - for field in field.actual_fields(self): - fieldname = field.name - if fieldname != 'eid' and ( - (eschema.has_subject_relation(fieldname) or - eschema.has_object_relation(fieldname))): - field.eidparam = True - self.fields.append(HiddenInitialValueField(field)) - return super(EntityFieldsForm, self).form_build_context(values) - - def form_field_value(self, field, load_bytes=False): - """return field's *typed* value - - overriden to deal with - * special eid / __type / edits- / edito- fields - * lookup for values on edited entities - """ - attr = field.name - entity = self.edited_entity - if attr == 'eid': - return entity.eid - if not field.eidparam: - return super(EntityFieldsForm, self).form_field_value(field, load_bytes) - if attr.startswith('edits-') or attr.startswith('edito-'): - # edit[s|o]- fieds must have the actual value stored on the entity - assert hasattr(field, 'visible_field') - vfield = field.visible_field - assert vfield.eidparam - if entity.has_eid(): - return self.form_field_value(vfield) - return INTERNAL_FIELD_VALUE - if attr == '__type': - return entity.id - if self.schema.rschema(attr).is_final(): - attrtype = entity.e_schema.destination(attr) - if attrtype == 'Password': - return entity.has_eid() and INTERNAL_FIELD_VALUE or '' - if attrtype == 'Bytes': - if entity.has_eid(): - if load_bytes: - return getattr(entity, attr) - # XXX value should reflect if some file is already attached - return True - return False - if entity.has_eid() or attr in entity: - value = getattr(entity, attr) - else: - value = self._form_field_default_value(field, load_bytes) - return value - # non final relation field - if entity.has_eid() or entity.relation_cached(attr, field.role): - value = [r[0] for r in entity.related(attr, field.role)] - else: - value = self._form_field_default_value(field, load_bytes) - return value - - def form_field_format(self, field): - """return MIME type used for the given (text or bytes) field""" - entity = self.edited_entity - if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and ( - entity.has_eid() or '%s_format' % field.name in entity): - return self.edited_entity.attr_metadata(field.name, 'format') - return self.req.property_value('ui.default-text-format') - - def form_field_encoding(self, field): - """return encoding used for the given (text) field""" - entity = self.edited_entity - if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and ( - entity.has_eid() or '%s_encoding' % field.name in entity): - return self.edited_entity.attr_metadata(field.name, 'encoding') - return super(EntityFieldsForm, self).form_field_encoding(field) - - def form_field_name(self, field): - """return qualified name for the given field""" - if field.eidparam: - return eid_param(field.name, self.edited_entity.eid) - return field.name - - def form_field_id(self, field): - """return dom id for the given field""" - if field.eidparam: - return eid_param(field.id, self.edited_entity.eid) - return field.id - - def form_field_vocabulary(self, field, limit=None): - """return vocabulary for the given field""" - role, rtype = field.role, field.name - method = '%s_%s_vocabulary' % (role, rtype) - try: - vocabfunc = getattr(self, method) - except AttributeError: - try: - # XXX bw compat, __vocabulary on the entity - vocabfunc = getattr(self.edited_entity, method) - except AttributeError: - vocabfunc = getattr(self, '%s_relation_vocabulary' % role) - else: - warn('found %s on %s, should be set on a specific form' - % (method, self.edited_entity.id), DeprecationWarning) - # NOTE: it is the responsibility of `vocabfunc` to sort the result - # (direclty through RQL or via a python sort). This is also - # important because `vocabfunc` might return a list with - # couples (label, None) which act as separators. In these - # cases, it doesn't make sense to sort results afterwards. - return vocabfunc(rtype, limit) - - def subject_relation_vocabulary(self, rtype, limit=None): - """defaut vocabulary method for the given relation, looking for - relation's object entities (i.e. self is the subject) - """ - entity = self.edited_entity - if isinstance(rtype, basestring): - rtype = entity.schema.rschema(rtype) - done = None - assert not rtype.is_final(), rtype - if entity.has_eid(): - done = set(e.eid for e in getattr(entity, str(rtype))) - result = [] - rsetsize = None - for objtype in rtype.objects(entity.e_schema): - if limit is not None: - rsetsize = limit - len(result) - result += self._relation_vocabulary(rtype, objtype, 'subject', - rsetsize, done) - if limit is not None and len(result) >= limit: - break - return result - - def object_relation_vocabulary(self, rtype, limit=None): - """defaut vocabulary method for the given relation, looking for - relation's subject entities (i.e. self is the object) - """ - entity = self.edited_entity - if isinstance(rtype, basestring): - rtype = entity.schema.rschema(rtype) - done = None - if entity.has_eid(): - done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype)) - result = [] - rsetsize = None - for subjtype in rtype.subjects(entity.e_schema): - if limit is not None: - rsetsize = limit - len(result) - result += self._relation_vocabulary(rtype, subjtype, 'object', - rsetsize, done) - if limit is not None and len(result) >= limit: - break - return result - - def subject_in_state_vocabulary(self, rtype, limit=None): - """vocabulary method for the in_state relation, looking for relation's - object entities (i.e. self is the subject) according to initial_state, - state_of and next_state relation - """ - entity = self.edited_entity - if not entity.has_eid() or not entity.in_state: - # get the initial state - rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S' - rset = self.req.execute(rql, {'etype': str(entity.e_schema)}) - if rset: - return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])] - return [] - results = [] - for tr in entity.in_state[0].transitions(entity): - state = tr.destination_state[0] - results.append((state.view('combobox'), state.eid)) - return sorted(results) - - -class CompositeForm(FieldsForm): - """form composed for sub-forms""" - form_renderer_id = 'composite' - - def __init__(self, *args, **kwargs): - super(CompositeForm, self).__init__(*args, **kwargs) - self.forms = [] - - def form_add_subform(self, subform): - """mark given form as a subform and append it""" - subform.is_subform = True - self.forms.append(subform) diff -r ea9eab290dcd -r e8032965f37a web/test/unittest_form.py --- a/web/test/unittest_form.py Fri May 29 14:07:42 2009 +0200 +++ b/web/test/unittest_form.py Fri May 29 14:19:30 2009 +0200 @@ -5,14 +5,16 @@ :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ + from logilab.common.testlib import unittest_main, mock_object + from cubicweb import Binary from cubicweb.devtools.testlib import WebTest -from cubicweb.web.form import EntityFieldsForm, FieldsForm from cubicweb.web.formfields import (IntField, StringField, RichTextField, DateTimeField, DateTimePicker, FileField, EditableFileField) from cubicweb.web.formwidgets import PasswordInput +from cubicweb.web.views.forms import EntityFieldsForm, FieldsForm from cubicweb.web.views.workflow import ChangeStateForm from cubicweb.web.views.formrenderers import FormRenderer diff -r ea9eab290dcd -r e8032965f37a web/test/unittest_formfields.py --- a/web/test/unittest_formfields.py Fri May 29 14:07:42 2009 +0200 +++ b/web/test/unittest_formfields.py Fri May 29 14:19:30 2009 +0200 @@ -12,9 +12,9 @@ from cubicweb.devtools import TestServerConfiguration from cubicweb.devtools.testlib import EnvBasedTC -from cubicweb.web.form import EntityFieldsForm from cubicweb.web.formwidgets import PasswordInput, TextArea, Select from cubicweb.web.formfields import * +from cubicweb.web.views.forms import EntityFieldsForm from cubes.file.entities import File diff -r ea9eab290dcd -r e8032965f37a web/views/autoform.py --- a/web/views/autoform.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/autoform.py Fri May 29 14:19:30 2009 +0200 @@ -12,13 +12,13 @@ from cubicweb import typed_eid from cubicweb.web import stdmsgs, uicfg -from cubicweb.web.form import FieldNotFound, EntityFieldsForm +from cubicweb.web.form import FieldNotFound from cubicweb.web.formfields import guess_field from cubicweb.web.formwidgets import Button, SubmitButton -from cubicweb.web.views.editforms import toggleable_relation_link, relation_id +from cubicweb.web.views import forms, editforms -class AutomaticEntityForm(EntityFieldsForm): +class AutomaticEntityForm(forms.EntityFieldsForm): """base automatic form to edit any entity. Designed to be fully generated from schema but highly configurable through: @@ -235,13 +235,13 @@ for label, rschema, role in self.srelations_by_category('generic', 'add'): relatedrset = entity.related(rschema, role, limit=self.related_limit) if rschema.has_perm(self.req, 'delete'): - toggleable_rel_link_func = toggleable_relation_link + toggleable_rel_link_func = editforms.toggleable_relation_link else: toggleable_rel_link_func = lambda x, y, z: u'' related = [] for row in xrange(relatedrset.rowcount): - nodeid = relation_id(entity.eid, rschema, role, - relatedrset[row][0]) + nodeid = editforms.relation_id(entity.eid, rschema, role, + relatedrset[row][0]) if nodeid in pending_deletes: status = u'pendingDelete' label = '+' diff -r ea9eab290dcd -r e8032965f37a web/views/cwproperties.py --- a/web/views/cwproperties.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/cwproperties.py Fri May 29 14:19:30 2009 +0200 @@ -17,7 +17,7 @@ match_user_groups) from cubicweb.view import StartupView from cubicweb.web import uicfg, stdmsgs -from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn +from cubicweb.web.form import FormViewMixIn from cubicweb.web.formfields import FIELDS, StringField from cubicweb.web.formwidgets import Select, Button, SubmitButton from cubicweb.web.views import primary, formrenderers @@ -189,10 +189,11 @@ def form(self, formid, keys, splitlabel=False): buttons = [SubmitButton()] - form = CompositeForm(self.req, domid=formid, action=self.build_url(), - form_buttons=buttons, - onsubmit="return validatePrefsForm('%s')" % formid, - submitmsg=self.req._('changes applied')) + form = self.vreg.select_object('forms', 'composite', self.req, + domid=formid, action=self.build_url(), + form_buttons=buttons, + onsubmit="return validatePrefsForm('%s')" % formid, + submitmsg=self.req._('changes applied')) path = self.req.relative_path() if '?' in path: path, params = path.split('?', 1) @@ -200,7 +201,8 @@ form.form_add_hidden('__redirectpath', path) for key in keys: self.form_row(form, key, splitlabel) - renderer = CWPropertiesFormRenderer(self.req, display_progress_div=False) + renderer = self.vreg.select_object('formrenderers', 'cwproperties', self.req, + display_progress_div=False) return form.form_render(renderer=renderer) def form_row(self, form, key, splitlabel): @@ -209,8 +211,8 @@ label = key.split('.')[-1] else: label = key - subform = EntityFieldsForm(self.req, entity=entity, set_error_url=False) - + subform = self.vreg.select_object('forms', 'base', self.req, entity=entity, + set_error_url=False) subform.append_field(PropertyValueField(name='value', label=label, eidparam=True)) subform.vreg = self.vreg diff -r ea9eab290dcd -r e8032965f37a web/views/editforms.py --- a/web/views/editforms.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/editforms.py Fri May 29 14:19:30 2009 +0200 @@ -21,10 +21,10 @@ from cubicweb.view import EntityView from cubicweb.common import tags from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param -from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn +from cubicweb.web.form import FormViewMixIn from cubicweb.web.formfields import RelationField from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton, Select -from cubicweb.web.views.formrenderers import FormRenderer +from cubicweb.web.views import forms def relation_id(eid, rtype, role, reid): @@ -59,17 +59,19 @@ % _('this action is not reversible!')) # XXX above message should have style of a warning w(u'

%s

\n' % _('Do you want to delete the following element(s) ?')) - form = CompositeForm(req, domid='deleteconf', copy_nav_params=True, - action=self.build_url('edit'), onsubmit=onsubmit, - form_buttons=[Button(stdmsgs.YES, cwaction='delete'), - Button(stdmsgs.NO, cwaction='cancel')]) + form = self.vreg.select_object('forms', 'composite', req, domid='deleteconf', + copy_nav_params=True, + action=self.build_url('edit'), onsubmit=onsubmit, + form_buttons=[Button(stdmsgs.YES, cwaction='delete'), + Button(stdmsgs.NO, cwaction='cancel')]) done = set() w(u'
    \n') for entity in self.rset.entities(): if entity.eid in done: continue done.add(entity.eid) - subform = EntityFieldsForm(req, entity=entity, set_error_url=False) + subform = self.vreg.select_object('forms', 'base', req, entity=entity, + set_error_url=False) form.form_add_subform(subform) # don't use outofcontext view or any other that may contain inline edition form w(u'
  • %s
  • ' % tags.a(entity.view('textoutofcontext'), @@ -118,10 +120,12 @@ form = self._build_relation_form(entity, value, rtype, role, row, col, vid, default) form.form_add_hidden(u'__maineid', entity.eid) - renderer = FormRenderer(self.req, display_label=False, display_help=False, - display_fields=[(rtype, role)], - table_class='', button_bar_class='buttonbar', - display_progress_div=False) + renderer = self.vreg.select_object('formrenderers', 'base', self.req, + entity=entity, + display_label=False, display_help=False, + display_fields=[(rtype, role)], + table_class='', button_bar_class='buttonbar', + display_progress_div=False) self.w(form.form_render(renderer=renderer)) def _build_relation_form(self, entity, value, rtype, role, row, col, vid, default): @@ -129,16 +133,17 @@ divid = 'd%s' % make_uid('%s-%s' % (rtype, entity.eid)) event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : vid, 'default' : default, 'role' : role} - form = EntityFieldsForm(self.req, entity=entity, action='#', - domid='%s-form' % divid, - cssstyle='display: none', - onsubmit=("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', " - "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');" % - event_data), - form_buttons=[SubmitButton(), - Button(stdmsgs.BUTTON_CANCEL, - onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" %\ - (entity.eid, rtype, divid))]) + onsubmit = ("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', " + "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');" + % event_data) + cancelclick = "cancelInlineEdit(%s,\'%s\',\'%s\')" % ( + entity.eid, rtype, divid) + form = self.vreg.select_object('forms', 'base', self.req, entity=entity, + domid='%s-form' % divid, cssstyle='display: none', + onsubmit=onsubmit, action='#', + form_buttons=[SubmitButton(), + Button(stdmsgs.BUTTON_CANCEL, + onclick=cancelclick)]) form.append_field(RelationField(name=rtype, role=role, sort=True, widget=Select(), label=u' ')) @@ -289,7 +294,7 @@ return self.req._('entity copied') -class TableEditForm(CompositeForm): +class TableEditForm(forms.CompositeForm): id = 'muledit' domid = 'entityForm' onsubmit = "return validateForm('%s', null);" % domid diff -r ea9eab290dcd -r e8032965f37a web/views/formrenderers.py --- a/web/views/formrenderers.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/formrenderers.py Fri May 29 14:19:30 2009 +0200 @@ -210,6 +210,13 @@ w(u'') +class BaseFormRenderer(FormRenderer): + """use form_renderer_id = 'base' if you want base FormRenderer without + adaptation by selection + """ + id = 'base' + + class HTableFormRenderer(FormRenderer): """display fields horizontally in a table @@ -298,10 +305,6 @@ w(u'%s' % form.req._(field.label)) w(u'') -class BaseFormRenderer(FormRenderer): - """use form_renderer_id = 'base' if you don't want adaptation by selection - """ - id = 'base' class EntityFormRenderer(FormRenderer): """specific renderer for entity edition form (edition)""" diff -r ea9eab290dcd -r e8032965f37a web/views/forms.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/views/forms.py Fri May 29 14:19:30 2009 +0200 @@ -0,0 +1,524 @@ +"""some base form classes for CubicWeb web client + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses +""" +__docformat__ = "restructuredtext en" + +from warnings import warn + +from logilab.common.compat import any +from logilab.common.decorators import iclassmethod + +from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset +from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param +from cubicweb.web import form, formwidgets as fwdgs +from cubicweb.web.controller import NAV_FORM_PARAMETERS +from cubicweb.web.formfields import HiddenInitialValueField, StringField + + +class FieldsForm(form.Form): + id = 'base' + + is_subform = False + + # attributes overrideable through __init__ + internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS + needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',) + needs_css = ('cubicweb.form.css',) + domid = 'form' + title = None + action = None + onsubmit = "return freezeFormButtons('%(domid)s');" + cssclass = None + cssstyle = None + cwtarget = None + redirect_path = None + set_error_url = True + copy_nav_params = False + form_buttons = None # form buttons (button widgets instances) + form_renderer_id = 'default' + + def __init__(self, req, rset=None, row=None, col=None, submitmsg=None, + **kwargs): + super(FieldsForm, self).__init__(req, rset, row=row, col=col) + self.fields = list(self.__class__._fields_) + for key, val in kwargs.items(): + if key in NAV_FORM_PARAMETERS: + self.form_add_hidden(key, val) + else: + assert hasattr(self.__class__, key) and not key[0] == '_', key + setattr(self, key, val) + if self.set_error_url: + self.form_add_hidden('__errorurl', self.session_key()) + if self.copy_nav_params: + for param in NAV_FORM_PARAMETERS: + if not param in kwargs: + value = req.form.get(param) + if value: + self.form_add_hidden(param, value) + if submitmsg is not None: + self.form_add_hidden('__message', submitmsg) + self.context = None + if 'domid' in kwargs:# session key changed + self.restore_previous_post(self.session_key()) + + @iclassmethod + def _fieldsattr(cls_or_self): + if isinstance(cls_or_self, type): + fields = cls_or_self._fields_ + else: + fields = cls_or_self.fields + return fields + + @iclassmethod + def field_by_name(cls_or_self, name, role='subject'): + """return field with the given name and role. + Raise FieldNotFound if the field can't be found. + """ + for field in cls_or_self._fieldsattr(): + if field.name == name and field.role == role: + return field + raise form.FieldNotFound(name) + + @iclassmethod + def fields_by_name(cls_or_self, name, role='subject'): + """return a list of fields with the given name and role""" + return [field for field in cls_or_self._fieldsattr() + if field.name == name and field.role == role] + + @iclassmethod + def remove_field(cls_or_self, field): + """remove a field from form class or instance""" + cls_or_self._fieldsattr().remove(field) + + @iclassmethod + def append_field(cls_or_self, field): + """append a field to form class or instance""" + cls_or_self._fieldsattr().append(field) + + @iclassmethod + def insert_field_before(cls_or_self, new_field, name, role='subject'): + field = cls_or_self.field_by_name(name, role) + fields = cls_or_self._fieldsattr() + fields.insert(fields.index(field), new_field) + + @iclassmethod + def insert_field_after(cls_or_self, new_field, name, role='subject'): + field = cls_or_self.field_by_name(name, role) + fields = cls_or_self._fieldsattr() + fields.insert(fields.index(field)+1, new_field) + + @property + def form_needs_multipart(self): + """true if the form needs enctype=multipart/form-data""" + return any(field.needs_multipart for field in self.fields) + + def form_add_hidden(self, name, value=None, **kwargs): + """add an hidden field to the form""" + field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value, + **kwargs) + if 'id' in kwargs: + # by default, hidden input don't set id attribute. If one is + # explicitly specified, ensure it will be set + field.widget.setdomid = True + self.append_field(field) + return field + + def add_media(self): + """adds media (CSS & JS) required by this widget""" + if self.needs_js: + self.req.add_js(self.needs_js) + if self.needs_css: + self.req.add_css(self.needs_css) + + def form_render(self, **values): + """render this form, using the renderer given in args or the default + FormRenderer() + """ + renderer = values.pop('renderer', None) + if renderer is None: + renderer = self.form_default_renderer() + return renderer.render(self, values) + + def form_default_renderer(self): + return self.vreg.select_object('formrenderers', self.form_renderer_id, + self.req, self.rset, + row=self.row, col=self.col) + + def form_build_context(self, rendervalues=None): + """build form context values (the .context attribute which is a + dictionary with field instance as key associated to a dictionary + containing field 'name' (qualified), 'id', 'value' (for display, always + a string). + + rendervalues is an optional dictionary containing extra kwargs given to + form_render() + """ + self.context = context = {} + # ensure rendervalues is a dict + if rendervalues is None: + rendervalues = {} + # use a copy in case fields are modified while context is build (eg + # __linkto handling for instance) + for field in self.fields[:]: + for field in field.actual_fields(self): + field.form_init(self) + value = self.form_field_display_value(field, rendervalues) + context[field] = {'value': value, + 'name': self.form_field_name(field), + 'id': self.form_field_id(field), + } + + def form_field_display_value(self, field, rendervalues, load_bytes=False): + """return field's *string* value to use for display + + looks in + 1. previously submitted form values if any (eg on validation error) + 2. req.form + 3. extra kw args given to render_form + 4. field's typed value + + values found in 1. and 2. are expected te be already some 'display' + value while those found in 3. and 4. are expected to be correctly typed. + """ + value = self._req_display_value(field) + if value is None: + if field.name in rendervalues: + value = rendervalues[field.name] + else: + value = self.form_field_value(field, load_bytes) + if callable(value): + value = value(self) + if value != INTERNAL_FIELD_VALUE: + value = field.format_value(self.req, value) + return value + + def _req_display_value(self, field): + qname = self.form_field_name(field) + if qname in self.form_previous_values: + return self.form_previous_values[qname] + if qname in self.req.form: + return self.req.form[qname] + if field.name in self.req.form: + return self.req.form[field.name] + return None + + def form_field_value(self, field, load_bytes=False): + """return field's *typed* value""" + myattr = '%s_%s_default' % (field.role, field.name) + if hasattr(self, myattr): + return getattr(self, myattr)() + value = field.initial + if callable(value): + value = value(self) + return value + + def form_field_error(self, field): + """return validation error for widget's field, if any""" + if self._field_has_error(field): + self.form_displayed_errors.add(field.name) + return u'%s' % self.form_valerror.errors[field.name] + return u'' + + def form_field_format(self, field): + """return MIME type used for the given (text or bytes) field""" + return self.req.property_value('ui.default-text-format') + + def form_field_encoding(self, field): + """return encoding used for the given (text) field""" + return self.req.encoding + + def form_field_name(self, field): + """return qualified name for the given field""" + return field.name + + def form_field_id(self, field): + """return dom id for the given field""" + return field.id + + def form_field_vocabulary(self, field, limit=None): + """return vocabulary for the given field. Should be overriden in + specific forms using fields which requires some vocabulary + """ + raise NotImplementedError + + def _field_has_error(self, field): + """return true if the field has some error in given validation exception + """ + return self.form_valerror and field.name in self.form_valerror.errors + + +class EntityFieldsForm(FieldsForm): + id = 'base' + __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity())) + + internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid') + domid = 'entityForm' + + def __init__(self, *args, **kwargs): + self.edited_entity = kwargs.pop('entity', None) + msg = kwargs.pop('submitmsg', None) + super(EntityFieldsForm, self).__init__(*args, **kwargs) + if self.edited_entity is None: + self.edited_entity = self.complete_entity(self.row or 0, self.col or 0) + self.form_add_hidden('__type', eidparam=True) + self.form_add_hidden('eid') + if msg: + # If we need to directly attach the new object to another one + self.form_add_hidden('__message', msg) + if not self.is_subform: + for linkto in self.req.list_form_param('__linkto'): + self.form_add_hidden('__linkto', linkto) + msg = '%s %s' % (msg, self.req._('and linked')) + + def _field_has_error(self, field): + """return true if the field has some error in given validation exception + """ + return super(EntityFieldsForm, self)._field_has_error(field) \ + and self.form_valerror.eid == self.edited_entity.eid + + def _relation_vocabulary(self, rtype, targettype, role, + limit=None, done=None): + """return unrelated entities for a given relation and target entity type + for use in vocabulary + """ + if done is None: + done = set() + rset = self.edited_entity.unrelated(rtype, targettype, role, limit) + res = [] + for entity in rset.entities(): + if entity.eid in done: + continue + done.add(entity.eid) + res.append((entity.view('combobox'), entity.eid)) + return res + + def _req_display_value(self, field): + value = super(EntityFieldsForm, self)._req_display_value(field) + if value is None: + value = self.edited_entity.linked_to(field.name, field.role) + if value: + searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role) + for eid in value] + # remove associated __linkto hidden fields + for field in self.fields_by_name('__linkto'): + if field.initial in searchedvalues: + self.remove_field(field) + else: + value = None + return value + + def _form_field_default_value(self, field, load_bytes): + defaultattr = 'default_%s' % field.name + if hasattr(self.edited_entity, defaultattr): + # XXX bw compat, default_ on the entity + warn('found %s on %s, should be set on a specific form' + % (defaultattr, self.edited_entity.id), DeprecationWarning) + value = getattr(self.edited_entity, defaultattr) + if callable(value): + value = value() + else: + value = super(EntityFieldsForm, self).form_field_value(field, + load_bytes) + return value + + def form_default_renderer(self): + return self.vreg.select_object('formrenderers', self.form_renderer_id, + self.req, self.rset, + row=self.row, col=self.col, + entity=self.edited_entity) + + def form_build_context(self, values=None): + """overriden to add edit[s|o] hidden fields and to ensure schema fields + have eidparam set to True + + edit[s|o] hidden fields are used to indicate the value for the + associated field before the (potential) modification made when + submitting the form. + """ + eschema = self.edited_entity.e_schema + for field in self.fields[:]: + for field in field.actual_fields(self): + fieldname = field.name + if fieldname != 'eid' and ( + (eschema.has_subject_relation(fieldname) or + eschema.has_object_relation(fieldname))): + field.eidparam = True + self.fields.append(HiddenInitialValueField(field)) + return super(EntityFieldsForm, self).form_build_context(values) + + def form_field_value(self, field, load_bytes=False): + """return field's *typed* value + + overriden to deal with + * special eid / __type / edits- / edito- fields + * lookup for values on edited entities + """ + attr = field.name + entity = self.edited_entity + if attr == 'eid': + return entity.eid + if not field.eidparam: + return super(EntityFieldsForm, self).form_field_value(field, load_bytes) + if attr.startswith('edits-') or attr.startswith('edito-'): + # edit[s|o]- fieds must have the actual value stored on the entity + assert hasattr(field, 'visible_field') + vfield = field.visible_field + assert vfield.eidparam + if entity.has_eid(): + return self.form_field_value(vfield) + return INTERNAL_FIELD_VALUE + if attr == '__type': + return entity.id + if self.schema.rschema(attr).is_final(): + attrtype = entity.e_schema.destination(attr) + if attrtype == 'Password': + return entity.has_eid() and INTERNAL_FIELD_VALUE or '' + if attrtype == 'Bytes': + if entity.has_eid(): + if load_bytes: + return getattr(entity, attr) + # XXX value should reflect if some file is already attached + return True + return False + if entity.has_eid() or attr in entity: + value = getattr(entity, attr) + else: + value = self._form_field_default_value(field, load_bytes) + return value + # non final relation field + if entity.has_eid() or entity.relation_cached(attr, field.role): + value = [r[0] for r in entity.related(attr, field.role)] + else: + value = self._form_field_default_value(field, load_bytes) + return value + + def form_field_format(self, field): + """return MIME type used for the given (text or bytes) field""" + entity = self.edited_entity + if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and ( + entity.has_eid() or '%s_format' % field.name in entity): + return self.edited_entity.attr_metadata(field.name, 'format') + return self.req.property_value('ui.default-text-format') + + def form_field_encoding(self, field): + """return encoding used for the given (text) field""" + entity = self.edited_entity + if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and ( + entity.has_eid() or '%s_encoding' % field.name in entity): + return self.edited_entity.attr_metadata(field.name, 'encoding') + return super(EntityFieldsForm, self).form_field_encoding(field) + + def form_field_name(self, field): + """return qualified name for the given field""" + if field.eidparam: + return eid_param(field.name, self.edited_entity.eid) + return field.name + + def form_field_id(self, field): + """return dom id for the given field""" + if field.eidparam: + return eid_param(field.id, self.edited_entity.eid) + return field.id + + def form_field_vocabulary(self, field, limit=None): + """return vocabulary for the given field""" + role, rtype = field.role, field.name + method = '%s_%s_vocabulary' % (role, rtype) + try: + vocabfunc = getattr(self, method) + except AttributeError: + try: + # XXX bw compat, __vocabulary on the entity + vocabfunc = getattr(self.edited_entity, method) + except AttributeError: + vocabfunc = getattr(self, '%s_relation_vocabulary' % role) + else: + warn('found %s on %s, should be set on a specific form' + % (method, self.edited_entity.id), DeprecationWarning) + # NOTE: it is the responsibility of `vocabfunc` to sort the result + # (direclty through RQL or via a python sort). This is also + # important because `vocabfunc` might return a list with + # couples (label, None) which act as separators. In these + # cases, it doesn't make sense to sort results afterwards. + return vocabfunc(rtype, limit) + + def subject_relation_vocabulary(self, rtype, limit=None): + """defaut vocabulary method for the given relation, looking for + relation's object entities (i.e. self is the subject) + """ + entity = self.edited_entity + if isinstance(rtype, basestring): + rtype = entity.schema.rschema(rtype) + done = None + assert not rtype.is_final(), rtype + if entity.has_eid(): + done = set(e.eid for e in getattr(entity, str(rtype))) + result = [] + rsetsize = None + for objtype in rtype.objects(entity.e_schema): + if limit is not None: + rsetsize = limit - len(result) + result += self._relation_vocabulary(rtype, objtype, 'subject', + rsetsize, done) + if limit is not None and len(result) >= limit: + break + return result + + def object_relation_vocabulary(self, rtype, limit=None): + """defaut vocabulary method for the given relation, looking for + relation's subject entities (i.e. self is the object) + """ + entity = self.edited_entity + if isinstance(rtype, basestring): + rtype = entity.schema.rschema(rtype) + done = None + if entity.has_eid(): + done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype)) + result = [] + rsetsize = None + for subjtype in rtype.subjects(entity.e_schema): + if limit is not None: + rsetsize = limit - len(result) + result += self._relation_vocabulary(rtype, subjtype, 'object', + rsetsize, done) + if limit is not None and len(result) >= limit: + break + return result + + def subject_in_state_vocabulary(self, rtype, limit=None): + """vocabulary method for the in_state relation, looking for relation's + object entities (i.e. self is the subject) according to initial_state, + state_of and next_state relation + """ + entity = self.edited_entity + if not entity.has_eid() or not entity.in_state: + # get the initial state + rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S' + rset = self.req.execute(rql, {'etype': str(entity.e_schema)}) + if rset: + return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])] + return [] + results = [] + for tr in entity.in_state[0].transitions(entity): + state = tr.destination_state[0] + results.append((state.view('combobox'), state.eid)) + return sorted(results) + + +class CompositeForm(FieldsForm): + """form composed for sub-forms""" + id = 'composite' + form_renderer_id = id + + def __init__(self, *args, **kwargs): + super(CompositeForm, self).__init__(*args, **kwargs) + self.forms = [] + + def form_add_subform(self, subform): + """mark given form as a subform and append it""" + subform.is_subform = True + self.forms.append(subform) diff -r ea9eab290dcd -r e8032965f37a web/views/management.py --- a/web/views/management.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/management.py Fri May 29 14:19:30 2009 +0200 @@ -15,13 +15,12 @@ from cubicweb.view import AnyRsetView, StartupView, EntityView from cubicweb.common.uilib import html_traceback, rest_traceback from cubicweb.web import formwidgets -from cubicweb.web.form import FieldsForm, EntityFieldsForm from cubicweb.web.formfields import guess_field -from cubicweb.web.views.formrenderers import HTableFormRenderer SUBMIT_MSGID = _('Submit bug report') MAIL_SUBMIT_MSGID = _('Submit bug report by mail') + class SecurityViewMixIn(object): """display security information for a given schema """ @@ -106,11 +105,13 @@ def owned_by_edit_form(self, entity): self.w('

    %s

    ' % self.req._('ownership')) msg = self.req._('ownerships have been changed') - form = EntityFieldsForm(self.req, None, entity=entity, submitmsg=msg, - form_buttons=[formwidgets.SubmitButton()], - domid='ownership%s' % entity.eid, - __redirectvid='security', - __redirectpath=entity.rest_path()) + form = self.vreg.select_object('forms', 'base', self.req, entity=entity, + form_renderer_id='base', + submitmsg=msg, + form_buttons=[formwidgets.SubmitButton()], + domid='ownership%s' % entity.eid, + __redirectvid='security', + __redirectpath=entity.rest_path()) field = guess_field(entity.e_schema, self.schema.rschema('owned_by')) form.append_field(field) self.w(form.form_render(display_progress_div=False)) @@ -163,11 +164,11 @@ newperm = self.vreg.etype_class('CWPermission')(self.req, None) newperm.eid = self.req.varmaker.next() w(u'

    %s

    ' % _('add a new permission')) - form = EntityFieldsForm(self.req, None, entity=newperm, - form_buttons=[formwidgets.SubmitButton()], - domid='reqperm%s' % entity.eid, - __redirectvid='security', - __redirectpath=entity.rest_path()) + form = self.vreg.select_object('forms', 'base', self.req, entity=newperm, + form_buttons=[formwidgets.SubmitButton()], + domid='reqperm%s' % entity.eid, + __redirectvid='security', + __redirectpath=entity.rest_path()) form.form_add_hidden('require_permission', entity.eid, role='object', eidparam=True) permnames = getattr(entity, '__permissions__', None) @@ -183,7 +184,8 @@ form.append_field(field) field = guess_field(cwpermschema, self.schema.rschema('require_group')) form.append_field(field) - renderer = HTableFormRenderer(self.req, display_progress_div=False) + renderer = self.select_object('formrenderers', 'htable', self.req, + display_progress_div=False) self.w(form.form_render(renderer=renderer)) @@ -240,7 +242,7 @@ submiturl = self.config['submit-url'] submitmail = self.config['submit-mail'] if submiturl or submitmail: - form = FieldsForm(self.req, set_error_url=False) + form = self.select_object('forms', 'base', self.req, set_error_url=False) binfo = text_error_description(ex, excinfo, req, eversion, cversions) form.form_add_hidden('description', binfo) form.form_add_hidden('__bugreporting', '1') diff -r ea9eab290dcd -r e8032965f37a web/views/massmailing.py --- a/web/views/massmailing.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/massmailing.py Fri May 29 14:19:30 2009 +0200 @@ -15,10 +15,10 @@ from cubicweb.view import EntityView from cubicweb.web import stdmsgs from cubicweb.web.action import Action -from cubicweb.web.form import FieldsForm, FormViewMixIn +from cubicweb.web.form import FormViewMixIn from cubicweb.web.formfields import StringField from cubicweb.web.formwidgets import CheckBox, TextInput, AjaxWidget, ImgButton -from cubicweb.web.views import formrenderers +from cubicweb.web.views import forms, formrenderers class SendEmailAction(Action): @@ -37,12 +37,12 @@ **params) -class MassMailingForm(FieldsForm): +class MassMailingForm(forms.FieldsForm): id = 'massmailing' sender = StringField(widget=TextInput({'disabled': 'disabled'}), label=_('From:')) recipient = StringField(widget=CheckBox(), label=_('Recipients:')) - subject = StringField(label=_('Subject:')) + subject = StringField(label=_('Subject:'), max_length=256) mailbody = StringField(widget=AjaxWidget(wdgtype='TemplateTextField', inputid='mailbody')) diff -r ea9eab290dcd -r e8032965f37a web/views/workflow.py --- a/web/views/workflow.py Fri May 29 14:07:42 2009 +0200 +++ b/web/views/workflow.py Fri May 29 14:19:30 2009 +0200 @@ -22,13 +22,12 @@ from cubicweb.web.form import FormViewMixIn from cubicweb.web.formfields import StringField, RichTextField from cubicweb.web.formwidgets import HiddenInput, SubmitButton, Button -from cubicweb.web.views import TmpFileViewMixin -from cubicweb.web.views.boxes import EditBox +from cubicweb.web.views import TmpFileViewMixin, forms # IWorkflowable views ######################################################### -class ChangeStateForm(form.EntityFieldsForm): +class ChangeStateForm(forms.EntityFieldsForm): id = 'changestate' form_renderer_id = 'base' # don't want EntityFormRenderer