--- a/web/views/autoform.py Tue Jan 26 20:22:13 2010 +0100
+++ b/web/views/autoform.py Tue Jan 26 20:25:56 2010 +0100
@@ -9,14 +9,16 @@
__docformat__ = "restructuredtext en"
_ = unicode
-from logilab.common.decorators import cached, iclassmethod
+from logilab.common.decorators import iclassmethod
from cubicweb import typed_eid
-from cubicweb.web import stdmsgs, uicfg
-from cubicweb.web import form, formwidgets as fwdgs
+from cubicweb.web import stdmsgs, uicfg, form, \
+ formwidgets as fw, formfields as ff
from cubicweb.web.views import forms, editforms, editviews
-_afs = uicfg.autoform_section
+_AFS = uicfg.autoform_section
+_AFFK = uicfg.autoform_field_kwargs
+
class AutomaticEntityForm(forms.EntityFieldsForm):
"""base automatic form to edit any entity.
@@ -35,9 +37,9 @@
cwtarget = 'eformframe'
cssclass = 'entityForm'
copy_nav_params = True
- form_buttons = [fwdgs.SubmitButton(),
- fwdgs.Button(stdmsgs.BUTTON_APPLY, cwaction='apply'),
- fwdgs.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
+ form_buttons = [fw.SubmitButton(),
+ fw.Button(stdmsgs.BUTTON_APPLY, cwaction='apply'),
+ fw.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
# for attributes selection when searching in uicfg.autoform_section
formtype = 'main'
# set this to a list of [(relation, role)] if you want to explictily tell
@@ -89,14 +91,31 @@
except form.FieldNotFound:
# meta attribute such as <attr>_format
continue
- if self.formtype == 'main' and entity.has_eid() and (
- self.display_fields is None or
- '_cw_generic_field' in self.display_fields):
- try:
- self.fields.append(self.field_by_name('_cw_generic_field'))
- except form.FieldNotFound:
- # no editable relation
- pass
+ if self.formtype == 'main':
+ if self.fieldsets_in_order:
+ fsio = list(self.fieldsets_in_order)
+ else:
+ fsio = [None]
+ self.fieldsets_in_order = fsio
+ # add fields for relation whose target should have an inline form
+ for formview in self.inlined_form_views():
+ field = self._inlined_form_view_field(formview)
+ self.fields.append(field)
+ if not field.fieldset in fsio:
+ fsio.append(field.fieldset)
+ # add the generic relation field if necessary
+ if entity.has_eid() and (
+ self.display_fields is None or
+ '_cw_generic_field' in self.display_fields):
+ try:
+ field = self.field_by_name('_cw_generic_field')
+ except form.FieldNotFound:
+ # no editable relation
+ pass
+ else:
+ self.fields.append(field)
+ if not field.fieldset in fsio:
+ fsio.append(field.fieldset)
self.maxrelitems = self._cw.property_value('navigation.related-limit')
self.force_display = bool(self._cw.form.get('__force_display'))
fnum = len(self.fields)
@@ -108,42 +127,6 @@
return None
return self.maxrelitems + 1
- @property
- def needs_multipart(self):
- """true if the form needs enctype=multipart/form-data"""
- return self._subform_needs_multipart()
-
- def build_context(self, rendervalues=None):
- super(AutomaticEntityForm, self).build_context(rendervalues)
- for form in self.inlined_forms():
- form.build_context(rendervalues)
-
- def _subform_needs_multipart(self, _tested=None):
- if _tested is None:
- _tested = set()
- if super(AutomaticEntityForm, self).needs_multipart:
- return True
- # take a look at inlined forms to check (recursively) if they
- # need multipart handling.
- # XXX: this is very suboptimal because inlined forms will be
- # selected / instantiated twice : here and during form rendering.
- # Potential solutions:
- # -> use subforms for inlined forms to get easiser access
- # -> use a simple onload js function to check if there is
- # a input type=file in the form
- # -> generate the <form> node when the content is rendered
- # and we know the correct enctype (formrenderer's w attribute
- # is not a StringIO)
- for formview in self.inlined_form_views():
- if formview.form:
- if hasattr(formview.form, '_subform_needs_multipart'):
- needs_multipart = formview.form._subform_needs_multipart(_tested)
- else:
- needs_multipart = formview.form.needs_multipart
- if needs_multipart:
- return True
- return False
-
def action(self):
"""return the form's action attribute. Default to validateform if not
explicitly overriden.
@@ -159,13 +142,38 @@
action = property(action, set_action)
+ # autoform specific fields #################################################
+
+ def _generic_relations_field(self):
+ try:
+ srels_by_cat = self.srelations_by_category('generic', 'add', strict=True)
+ warn('[3.6] %s: srelations_by_category is deprecated, use uicfg or '
+ 'override editable_relations instead' % classid(form),
+ DeprecationWarning)
+ except AttributeError:
+ srels_by_cat = self.editable_relations()
+ if not srels_by_cat:
+ raise form.FieldNotFound('_cw_generic_field')
+ fieldset = u'%s :' % self._cw.__('This %s' % self.edited_entity.e_schema)
+ fieldset = fieldset.capitalize()
+ return editviews.GenericRelationsField(self.editable_relations(),
+ fieldset=fieldset, label=None)
+
+ def _inlined_form_view_field(self, view):
+ # XXX allow more customization
+ kwargs = _AFFK.etype_get(self.edited_entity.e_schema, view.rtype,
+ view.role, view.etype)
+ if kwargs is None:
+ kwargs = {}
+ return editforms.InlinedFormField(view=view, **kwargs)
+
# methods mapping edited entity relations to fields in the form ############
def _relations_by_section(self, section, permission='add', strict=False):
"""return a list of (relation schema, target schemas, role) matching
given category(ies) and permission
"""
- return _afs.relations_by_section(
+ return _AFS.relations_by_section(
self.edited_entity, self.formtype, section, permission, strict)
def editable_attributes(self, strict=False):
@@ -194,13 +202,11 @@
"""
return self._relations_by_section('inlined')
- # generic relations modifier ###############################################
-
- # inlined forms support ####################################################
+ # inlined forms control ####################################################
- @cached
def inlined_form_views(self):
- """compute and return list of inlined form views (hosting the inlined form object)
+ """compute and return list of inlined form views (hosting the inlined
+ form object)
"""
allformviews = []
entity = self.edited_entity
@@ -231,11 +237,6 @@
allformviews += formviews
return allformviews
- def inlined_forms(self):
- for formview in self.inlined_form_views():
- if formview.form: # may be None for the addnew_link artefact form
- yield formview.form
-
def should_inline_relation_form(self, rschema, targettype, role):
"""return true if the given relation with entity has role and a
targettype target should be inlined
@@ -280,8 +281,9 @@
# display inline-edition view for all existing related entities
for i, relentity in enumerate(related.entities()):
if relentity.has_perm('update'):
- yield vvreg.select('inline-edition', self._cw, rset=related,
- row=i, col=0, rtype=rschema, role=role,
+ yield vvreg.select('inline-edition', self._cw,
+ rset=related, row=i, col=0,
+ etype=ttype, rtype=rschema, role=role,
peid=entity.eid, pform=self)
def inline_creation_form_view(self, rschema, ttype, role):
@@ -295,46 +297,45 @@
## default form ui configuration ##############################################
-_afs = uicfg.autoform_section
# use primary and not generated for eid since it has to be an hidden
-_afs.tag_attribute(('*', 'eid'), 'main', 'attributes')
-_afs.tag_attribute(('*', 'eid'), 'muledit', 'attributes')
-_afs.tag_attribute(('*', 'description'), 'main', 'attributes')
-_afs.tag_attribute(('*', 'creation_date'), 'main', 'metadata')
-_afs.tag_attribute(('*', 'modification_date'), 'main', 'metadata')
-_afs.tag_attribute(('*', 'cwuri'), 'main', 'metadata')
-_afs.tag_attribute(('*', 'has_text'), 'main', 'hidden')
-_afs.tag_subject_of(('*', 'in_state', '*'), 'main', 'hidden')
-_afs.tag_subject_of(('*', 'owned_by', '*'), 'main', 'metadata')
-_afs.tag_subject_of(('*', 'created_by', '*'), 'main', 'metadata')
-_afs.tag_subject_of(('*', 'require_permission', '*'), 'main', 'hidden')
-_afs.tag_subject_of(('*', 'by_transition', '*'), 'main', 'attributes')
-_afs.tag_subject_of(('*', 'by_transition', '*'), 'muledit', 'attributes')
-_afs.tag_object_of(('*', 'by_transition', '*'), 'main', 'hidden')
-_afs.tag_object_of(('*', 'from_state', '*'), 'main', 'hidden')
-_afs.tag_object_of(('*', 'to_state', '*'), 'main', 'hidden')
-_afs.tag_subject_of(('*', 'wf_info_for', '*'), 'main', 'attributes')
-_afs.tag_subject_of(('*', 'wf_info_for', '*'), 'muledit', 'attributes')
-_afs.tag_object_of(('*', 'wf_info_for', '*'), 'main', 'hidden')
-_afs.tag_subject_of(('CWPermission', 'require_group', '*'), 'main', 'attributes')
-_afs.tag_subject_of(('CWPermission', 'require_group', '*'), 'muledit', 'attributes')
-_afs.tag_attribute(('CWEType', 'final'), 'main', 'hidden')
-_afs.tag_attribute(('CWRType', 'final'), 'main', 'hidden')
-_afs.tag_attribute(('CWUser', 'firstname'), 'main', 'attributes')
-_afs.tag_attribute(('CWUser', 'surname'), 'main', 'attributes')
-_afs.tag_attribute(('CWUser', 'last_login_time'), 'main', 'metadata')
-_afs.tag_subject_of(('CWUser', 'in_group', '*'), 'main', 'attributes')
-_afs.tag_subject_of(('CWUser', 'in_group', '*'), 'muledit', 'attributes')
-_afs.tag_subject_of(('*', 'primary_email', '*'), 'main', 'relations')
-_afs.tag_subject_of(('*', 'use_email', '*'), 'main', 'inlined')
-_afs.tag_subject_of(('CWRelation', 'relation_type', '*'), 'main', 'inlined')
-_afs.tag_subject_of(('CWRelation', 'from_entity', '*'), 'main', 'inlined')
-_afs.tag_subject_of(('CWRelation', 'to_entity', '*'), 'main', 'inlined')
+_AFS.tag_attribute(('*', 'eid'), 'main', 'attributes')
+_AFS.tag_attribute(('*', 'eid'), 'muledit', 'attributes')
+_AFS.tag_attribute(('*', 'description'), 'main', 'attributes')
+_AFS.tag_attribute(('*', 'creation_date'), 'main', 'metadata')
+_AFS.tag_attribute(('*', 'modification_date'), 'main', 'metadata')
+_AFS.tag_attribute(('*', 'cwuri'), 'main', 'metadata')
+_AFS.tag_attribute(('*', 'has_text'), 'main', 'hidden')
+_AFS.tag_subject_of(('*', 'in_state', '*'), 'main', 'hidden')
+_AFS.tag_subject_of(('*', 'owned_by', '*'), 'main', 'metadata')
+_AFS.tag_subject_of(('*', 'created_by', '*'), 'main', 'metadata')
+_AFS.tag_subject_of(('*', 'require_permission', '*'), 'main', 'hidden')
+_AFS.tag_subject_of(('*', 'by_transition', '*'), 'main', 'attributes')
+_AFS.tag_subject_of(('*', 'by_transition', '*'), 'muledit', 'attributes')
+_AFS.tag_object_of(('*', 'by_transition', '*'), 'main', 'hidden')
+_AFS.tag_object_of(('*', 'from_state', '*'), 'main', 'hidden')
+_AFS.tag_object_of(('*', 'to_state', '*'), 'main', 'hidden')
+_AFS.tag_subject_of(('*', 'wf_info_for', '*'), 'main', 'attributes')
+_AFS.tag_subject_of(('*', 'wf_info_for', '*'), 'muledit', 'attributes')
+_AFS.tag_object_of(('*', 'wf_info_for', '*'), 'main', 'hidden')
+_AFS.tag_subject_of(('CWPermission', 'require_group', '*'), 'main', 'attributes')
+_AFS.tag_subject_of(('CWPermission', 'require_group', '*'), 'muledit', 'attributes')
+_AFS.tag_attribute(('CWEType', 'final'), 'main', 'hidden')
+_AFS.tag_attribute(('CWRType', 'final'), 'main', 'hidden')
+_AFS.tag_attribute(('CWUser', 'firstname'), 'main', 'attributes')
+_AFS.tag_attribute(('CWUser', 'surname'), 'main', 'attributes')
+_AFS.tag_attribute(('CWUser', 'last_login_time'), 'main', 'metadata')
+_AFS.tag_subject_of(('CWUser', 'in_group', '*'), 'main', 'attributes')
+_AFS.tag_subject_of(('CWUser', 'in_group', '*'), 'muledit', 'attributes')
+_AFS.tag_subject_of(('*', 'primary_email', '*'), 'main', 'relations')
+_AFS.tag_subject_of(('*', 'use_email', '*'), 'main', 'inlined')
+_AFS.tag_subject_of(('CWRelation', 'relation_type', '*'), 'main', 'inlined')
+_AFS.tag_subject_of(('CWRelation', 'from_entity', '*'), 'main', 'inlined')
+_AFS.tag_subject_of(('CWRelation', 'to_entity', '*'), 'main', 'inlined')
-uicfg.autoform_field_kwargs.tag_attribute(('RQLExpression', 'expression'),
- {'widget': fwdgs.TextInput})
-uicfg.autoform_field_kwargs.tag_subject_of(('TrInfo', 'wf_info_for', '*'),
- {'widget': fwdgs.HiddenInput})
+_AFFK.tag_attribute(('RQLExpression', 'expression'),
+ {'widget': fw.TextInput})
+_AFFK.tag_subject_of(('TrInfo', 'wf_info_for', '*'),
+ {'widget': fw.HiddenInput})
def registration_callback(vreg):
global etype_relation_field
--- a/web/views/editforms.py Tue Jan 26 20:22:13 2010 +0100
+++ b/web/views/editforms.py Tue Jan 26 20:25:56 2010 +0100
@@ -21,10 +21,9 @@
specified_etype_implements, yes)
from cubicweb.view import EntityView
from cubicweb import tags
-from cubicweb.web import uicfg, stdmsgs, eid_param
+from cubicweb.web import uicfg, stdmsgs, eid_param, \
+ formfields as ff, formwidgets as fw
from cubicweb.web.form import FormViewMixIn, FieldNotFound
-from cubicweb.web.formfields import guess_field
-from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton
from cubicweb.web.views import forms
_pvdc = uicfg.primaryview_display_ctrl
@@ -36,8 +35,8 @@
domid = 'deleteconf'
copy_nav_params = True
- form_buttons = [Button(stdmsgs.BUTTON_DELETE, cwaction='delete'),
- Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
+ form_buttons = [fw.Button(stdmsgs.BUTTON_DELETE, cwaction='delete'),
+ fw.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
@property
def action(self):
return self._cw.build_url('edit')
@@ -230,8 +229,8 @@
form = self._cw.vreg['forms'].select(
formid, self._cw, entity=entity, domid='%s-form' % divid,
cssstyle='display: none', onsubmit=onsubmit, action='#',
- form_buttons=[SubmitButton(), Button(stdmsgs.BUTTON_CANCEL,
- onclick=cancelclick)],
+ form_buttons=[fw.SubmitButton(),
+ fw.Button(stdmsgs.BUTTON_CANCEL, onclick=cancelclick)],
**formargs)
form.event_args = event_args
return form
@@ -410,8 +409,8 @@
__regid__ = 'muledit'
domid = 'entityForm'
onsubmit = "return validateForm('%s', null);" % domid
- form_buttons = [SubmitButton(_('validate modifications on selected items')),
- ResetButton(_('revert changes'))]
+ form_buttons = [fw.SubmitButton(_('validate modifications on selected items')),
+ fw.ResetButton(_('revert changes'))]
def __init__(self, req, rset, **kwargs):
kwargs.setdefault('__redirectrql', rset.printable_rql())
@@ -450,6 +449,53 @@
self.w(form.render(formvid='edition'))
+# inlined form handling ########################################################
+
+class InlinedFormField(ff.Field):
+ def __init__(self, view=None, **kwargs):
+ if view.role == 'object':
+ fieldset = u'%s_object%s' % view.rtype
+ else:
+ fieldset = view.rtype
+ #kwargs.setdefault('fieldset', fieldset)
+ kwargs.setdefault('label', None)
+ super(InlinedFormField, self).__init__(name=view.rtype, role=view.role,
+ eidparam=True, **kwargs)
+ self.view = view
+
+ def render(self, form, renderer):
+ """render this field, which is part of form, using the given form
+ renderer
+ """
+ view = self.view
+ i18nctx = 'inlined:%s.%s.%s' % (form.edited_entity.e_schema,
+ view.rtype, view.role)
+ return u'<div class="inline-%s-%s-slot">%s</div>' % (
+ view.rtype, view.role,
+ view.render(i18nctx=i18nctx, row=view.cw_row, col=view.cw_col))
+
+ def form_init(self, form):
+ """method called before by build_context to trigger potential field
+ initialization requiring the form instance
+ """
+ if self.view.form:
+ self.view.form.build_context(form.formvalues)
+
+ @property
+ def needs_multipart(self):
+ if self.view.form:
+ # take a look at inlined forms to check (recursively) if they need
+ # multipart handling.
+ return self.view.form.needs_multipart
+ return False
+
+ def has_been_modified(self, form):
+ return False
+
+ def process_posted(self, form):
+ pass # handled by the subform
+
+
class InlineEntityEditionFormView(FormViewMixIn, EntityView):
"""
:attr peid: the parent entity's eid hosting the inline form
@@ -460,7 +506,7 @@
__regid__ = 'inline-edition'
__select__ = non_final_entity() & match_kwargs('peid', 'rtype')
- _select_attrs = ('peid', 'rtype', 'role', 'pform')
+ _select_attrs = ('peid', 'rtype', 'role', 'pform', 'etype')
removejs = "removeInlinedEntity('%s', '%s', '%s')"
def __init__(self, *args, **kwargs):
@@ -548,7 +594,6 @@
__regid__ = 'inline-creation'
__select__ = (match_kwargs('peid', 'rtype')
& specified_etype_implements('Any'))
- _select_attrs = InlineEntityEditionFormView._select_attrs + ('etype',)
@property
def removejs(self):
@@ -559,9 +604,11 @@
# we have to make this link appears back. This is done by giving add new link
# id to removeInlineForm.
if card not in '?1':
- return "removeInlineForm('%s', '%s', '%s')"
- divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role, self.peid)
- return "removeInlineForm('%%s', '%%s', '%%s', '%s')" % divid
+ return "removeInlineForm('%%s', '%%s', '%s', '%%s')" % self.role
+ divid = "addNew%s%s%s:%s" % (
+ self.etype, self.rtype, self.role, self.peid)
+ return "removeInlineForm('%%s', '%%s', '%s', '%%s', '%s')" % (
+ self.role, divid)
@cached
def _entity(self):