# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1235080130 -3600 # Node ID 168ad6d424d1fed88eeab4277e5d05532972f456 # Parent 5d993a0c794c6cb711e36d2885d9cb74553f4c39 form to edit multiple entities, use it in DeleteConfForm diff -r 5d993a0c794c -r 168ad6d424d1 web/form.py --- a/web/form.py Thu Feb 19 22:17:39 2009 +0100 +++ b/web/form.py Thu Feb 19 22:48:50 2009 +0100 @@ -20,7 +20,7 @@ from cubicweb.common.registerers import accepts_registerer from cubicweb.web import stdmsgs from cubicweb.web.httpcache import NoHTTPCacheManager -from cubicweb.web.controller import redirect_params +from cubicweb.web.controller import NAV_FORM_PARAMETERS, redirect_params from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param @@ -469,9 +469,9 @@ allfields += base._fields_ clsfields = (item for item in classdict.items() if isinstance(item[1], Field)) - for name, field in sorted(clsfields, key=lambda x: x[1].creation_rank): + for fieldname, field in sorted(clsfields, key=lambda x: x[1].creation_rank): if not field.name: - field.set_name(name) + field.set_name(fieldname) allfields.append(field) classdict['_fields_'] = allfields return super(metafieldsform, mcs).__new__(mcs, name, bases, classdict) @@ -481,25 +481,40 @@ __metaclass__ = metafieldsform def __init__(self, req, id=None, title=None, action='edit', - redirect_path=None): + onsubmit="return freezeFormButtons('%s');", + cssclass=None, cssstyle=None, cwtarget=None, buttons=None, + redirect_path=None, set_error_url=True, copy_nav_params=False): self.req = req self.id = id or 'form' self.title = title self.action = action + self.onsubmit = onsubmit + self.cssclass = cssclass + self.cssstyle = cssstyle + self.cwtarget = cwtarget self.redirect_path = None self.fields = list(self.__class__._fields_) - self.fields.append(TextField(name='__errorurl', widget=HiddenInput, - initial=req.url())) + if set_error_url: + self.form_add_hidden('__errorurl', req.url()) + if copy_nav_params: + for param in NAV_FORM_PARAMETERS: + value = req.form.get(param) + if value: + self.form_add_hidden('__errorurl', initial=value) + self.buttons = buttons or [] self.context = {} @property def form_needs_multipart(self): return any(field.needs_multipart for field in self.fields) + def form_add_hidden(self, name, value=None, **kwargs): + self.fields.append(TextField(name=name, widget=HiddenInput, + initial=value, **kwargs)) + def form_render(self, **values): renderer = values.pop('renderer', FormRenderer()) - self.form_build_context(values) - return renderer.render(self) + return renderer.render(self, values) def form_build_context(self, values): self.context = context = {} @@ -563,39 +578,43 @@ return u'' % ( label, tabindex or 4) - + def form_buttons(self): + return self.buttons + + class EntityFieldsForm(FieldsForm): def __init__(self, *args, **kwargs): kwargs.setdefault('id', 'entityForm') + self.entity = kwargs.pop('entity', None) super(EntityFieldsForm, self).__init__(*args, **kwargs) - self.fields.append(TextField(name='__type', widget=HiddenInput)) - self.fields.append(TextField(name='eid', widget=HiddenInput, - eidparam=False)) + self.form_add_hidden('__type') + self.form_add_hidden('eid', eidparam=False) def form_render(self, entity, **values): + self.form_add_entity_hiddens(entity.e_schema) self.entity = entity - eschema = self.entity.e_schema + return super(EntityFieldsForm, self).form_render(**values) + + def form_add_entity_hiddens(self, eschema): for field in self.fields[:]: fieldname = field.name if fieldname != 'eid' and ( (eschema.has_subject_relation(fieldname) or - eschema.has_object_relation(fieldname))): - self.fields.append(self.build_hidden_field(field)) - return super(EntityFieldsForm, self).form_render(**values) + eschema.has_object_relation(fieldname))): + self.fields.append(self.form_entity_hidden_field(field)) - def build_hidden_field(self, field): + def form_entity_hidden_field(self, field): """returns the hidden field which will indicate the value before the modification """ # Only RelationField has a `role` attribute, others are used # to describe attribute fields => role is 'subject' - role = getattr(field, 'role', 'subject') - if role == 'subject': + if getattr(field, 'role', 'subject') == 'subject': name = 'edits-%s' % field.name else: name = 'edito-%s' % field.name return HiddenInitialValueField(field, name=name) - + def form_field_value(self, field, values): """look for field's value with the following rules: 1. handle special __type and eid fields @@ -665,45 +684,74 @@ return zip((entity.req._(v) for v in choices), choices) return zip(choices, choices) - + +class MultipleFieldsForm(FieldsForm): + def __init__(self, *args, **kwargs): + super(MultipleFieldsForm, self).__init__(*args, **kwargs) + self.forms = [] + + def form_add_subform(self, subform): + self.forms.append(subform) + # form renderers ############ class FormRenderer(object): - def render(self, form): + def render(self, form, values): data = [] w = data.append # XXX form_needs_multipart - w(u'
' - % (form.req.build_url(form.action), form.id, form.id)) + print 'render', form + w(self.open_form(form)) w(u'
%s
' % _('validating...')) w(u'
') w(tags.input(type='hidden', name='__form_id', value=form.id)) if form.redirect_path: w(tags.input(type='hidden', name='__redirect_path', value=form.redirect_path)) - self.render_fields(w, form) + self.render_fields(w, form, values) self.render_buttons(w, form) w(u'
') w(u'
') return '\n'.join(data) - def render_fields(self, w, form): + def open_form(self, form): + if form.form_needs_multipart: + enctype = 'multipart/form-data' + else: + enctype = 'application/x-www-form-urlencoded' + tag = ('
' + + def render_fields(self, w, form, values): + form.form_build_context(values) fields = form.fields[:] for field in form.fields: if isinstance(field.widget, HiddenInput): w(field.render(form)) fields.remove(field) - w(u'') - for field in fields: - w(u'') - w('' % self.render_label(form, field)) - w(u'') - w(u'
%s') - w(field.render(form)) - w(u'
') - + if fields: + w(u'') + for field in fields: + w(u'') + w('' % self.render_label(form, field)) + w(u'') + w(u'
%s') + w(field.render(form)) + w(u'
') + for childform in getattr(form, 'forms', []): + self.render_fields(w, childform, values) + def render_buttons(self, w, form): - for button in form.buttons(): + for button in form.form_buttons(): w(button) def render_label(self, form, field): diff -r 5d993a0c794c -r 168ad6d424d1 web/test/unittest_form.py --- a/web/test/unittest_form.py Thu Feb 19 22:17:39 2009 +0100 +++ b/web/test/unittest_form.py Thu Feb 19 22:48:50 2009 +0100 @@ -1,29 +1,35 @@ from logilab.common.testlib import unittest_main, mock_object -from cubicweb.devtools.apptest import EnvBasedTC +from cubicweb.devtools.testlib import WebTest from cubicweb.web.form import * from cubicweb.web.views.baseforms import ChangeStateForm + class CustomChangeStateForm(ChangeStateForm): hello = IntField(name='youlou') creation_date = DateTimeField(widget=DateTimePicker) + -class EntityFieldsFormTC(EnvBasedTC): +class EntityFieldsFormTC(WebTest): def setUp(self): super(EntityFieldsFormTC, self).setUp() self.req = self.request() self.entity = self.user(self.req) - def test(self): + def test_form_inheritance(self): + form = CustomChangeStateForm(self.req, redirect_path='perdu.com') + self.assertEquals(form.form_render(self.entity, state=123), + ''' ''') + + def test_change_state_form(self): form = ChangeStateForm(self.req, redirect_path='perdu.com') self.assertEquals(form.form_render(self.entity, state=123), ''' ''') - def test_form_inheritance(self): - form = CustomChangeStateForm(self.req, redirect_path='perdu.com') - self.assertEquals(form.form_render(self.entity, state=123), - ''' ''') - + def test_delete_conf_form_multi(self): + rset = self.execute('EGroup X') + self.assertEquals(self.view('deleteconf', rset).source, + '') if __name__ == '__main__': unittest_main() diff -r 5d993a0c794c -r 168ad6d424d1 web/views/baseforms.py --- a/web/views/baseforms.py Thu Feb 19 22:17:39 2009 +0100 +++ b/web/views/baseforms.py Thu Feb 19 22:48:50 2009 +0100 @@ -20,6 +20,7 @@ non_final_entity, accepts_etype_compat) from cubicweb.utils import make_uid from cubicweb.view import View, EntityView +from cubicweb.common import tags from cubicweb.common.uilib import cut from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param from cubicweb.web.controller import NAV_FORM_PARAMETERS @@ -28,7 +29,11 @@ _ = unicode -class DeleteConfForm(FormMixIn, EntityView): +from cubicweb.web.form import MultipleFieldsForm, EntityFieldsForm, TextField, \ + RichTextField, HiddenInput + + +class DeleteConfForm(EntityView): id = 'deleteconf' title = _('delete') domid = 'deleteconf' @@ -39,67 +44,47 @@ def call(self): """ask for confirmation before real deletion""" - _ = self.req._ - self.req.add_js('cubicweb.edition.js') - self.w(u'\n' % _('this action is not reversible!')) + req, w = self.req, self.w + _ = req._ + req.add_js('cubicweb.edition.js') + w(u'\n' + % _('this action is not reversible!')) # XXX above message should have style of a warning - self.w(u'

%s

\n' % _('Do you want to delete the following element(s) ?')) - if self.onsubmit: - self.w(u'' - % (self.build_url(), self.onsubmit)) - else: - self.w(u'' - % (self.build_url())) - - self.w(u'
\n') - self.display_rset() - #self.w(u'' % self.req.form['rql']) - self.w(u'' % self.id) - self.w(self.button_delete(label=stdmsgs.YES)) - self.w(self.button_cancel(label=stdmsgs.NO)) - for param in NAV_FORM_PARAMETERS: - value = self.req.form.get(param) - if value: - self.w(u'' % (param, value)) - self.w(u'
\n') - - def display_rset(self): - self.w(u'\n') + w(form.form_render()) -from cubicweb.web.form import EntityFieldsForm, TextField, RichTextField, HiddenInput - class ChangeStateForm(EntityFieldsForm): state = TextField(widget=HiddenInput) - __method = TextField(widget=HiddenInput, initial='set_state') + __method = TextField(name='__method', initial='set_state', widget=HiddenInput) trcomment = RichTextField(eidparam=True) - def buttons(self): - return [self.button_ok(label=self.req._(stdmsgs.YES), + def form_buttons(self): + return [self.button_ok(label=stdmsgs.YES, tabindex=self.req.next_tabindex()), - self.button_cancel(label=self.req._(stdmsgs.NO), + self.button_cancel(label=stdmsgs.NO, tabindex=self.req.next_tabindex())] + -class ChangeStateFormView(FormMixIn, EntityView): +class ChangeStateFormView(EntityView): id = 'statuschange' title = _('status change') @@ -122,46 +107,6 @@ form = ChangeStateForm(redirect_path=self.redirectpath(entity)) # self.vreg.select_form('changestateform') self.w(form.form_render(req, entity, state=dest.eid)) - -# self.w(u'
\n' -# % self.build_url('edit')) -# self.w(u'
%s
' % _('validating...')) -# self.w(u'
\n') -# #self.w(u'\n' -# # % html_escape(self.req.url())) -# self.w(u'\n' % self.id) -# self.w(u'' % eid) -# self.w(u'\n' -# % (eid_param('__type', eid), entity.e_schema)) -# self.w(u'\n' -# % (eid_param('state', eid), dest.eid)) -# self.w(u'\n' -# % html_escape(self.redirectpath(entity))) -# self.fill_form(entity, state, dest) -# self.w(u'\n') -# self.w(self.button_ok(label=stdmsgs.YES, tabindex=self.req.next_tabindex())) -# self.w(self.button_cancel(label=stdmsgs.NO, tabindex=self.req.next_tabindex())) -# self.w(u'
\n') -# self.w(u'
') - -# def fill_form(self, entity, state, dest): -# # hack to use the widget for comment_format -# trinfo = self.vreg.etype_class('TrInfo')(self.req, None) -# # widget are cached, copy it since we want to modify its name attribute -# wdg = trinfo.get_widget('comment_format') -# wdg.name = 'trcommentformat' -# # set a value in entity to avoid lookup for a non existant attribute... -# trinfo['trcommentformat'] = u'' -# # comment format/content have to be grouped using the original entity eid -# wdg.rname = eid_param('trcommentformat', entity.eid) -# self.w(wdg.render_label(trinfo)) -# self.w(wdg._edit_render(trinfo)) -# self.w(u'
\n') -# cformname = eid_param('trcomment', entity.eid) -# self.w(u'\n' % (cformname, self.req._('comment:'))) -# self.w(u'
\n' -# % (cformname, self.req.next_tabindex())) - def redirectpath(self, entity): return entity.rest_path()