turn every form class into appobject. They should not be instantiated manually anymore.
--- 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)
--- 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):
--- 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)
--- 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'<span class="error">%s</span>' % 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_<field name> 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, <role>_<rtype>_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)
--- 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
--- 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
--- 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 = '+'
--- 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
--- 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'<h4>%s</h4>\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'<ul>\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'<li>%s</li>' % 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
--- 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'</tr></table>')
+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'<th>%s</th>' % form.req._(field.label))
w(u'</tr>')
-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)"""
--- /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'<span class="error">%s</span>' % 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_<field name> 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, <role>_<rtype>_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)
--- 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('<h3>%s</h3>' % 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'<p>%s</p>' % _('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')
--- 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'))
--- 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