diff -r 3d01229d9f6a -r a4eb20f86cb0 web/views/eproperties.py --- a/web/views/eproperties.py Wed Apr 08 20:37:55 2009 +0200 +++ b/web/views/eproperties.py Wed Apr 08 20:38:34 2009 +0200 @@ -10,18 +10,19 @@ from logilab.common.decorators import cached +from cubicweb import UnknownProperty from cubicweb.selectors import (one_line_rset, none_rset, implements, - match_user_groups) + match_user_groups, entity_implements) from cubicweb.utils import UStringIO from cubicweb.view import StartupView from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param from cubicweb.web.views import baseviews -from cubicweb.web.form import FormMixIn +from cubicweb.web import stdmsgs +from cubicweb.web.form import FormMixIn, CompositeForm, EntityFieldsForm +from cubicweb.web.formfields import FIELDS, StringField +from cubicweb.web.formwidgets import Select, Button, SubmitButton from cubicweb.web.views.editforms import AutomaticEntityForm -AutomaticEntityForm.rwidgets.set_rtag('PropertyKeyWidget', 'pkey', 'subject', 'EProperty') -AutomaticEntityForm.rwidgets.set_rtag('PropertyValueWidget', 'value', 'subject', 'EProperty') - _ = unicode # some string we want to be internationalizable for nicer display of eproperty @@ -33,10 +34,6 @@ _('components') _('contentnavigation') -class EPropertyPrimaryView(baseviews.PrimaryView): - __select__ = implements('EProperty') - skip_none = False - def make_togglable_link(nodeid, label, cookiename): """builds a HTML link that switches the visibility & remembers it""" @@ -47,9 +44,15 @@ def css_class(someclass): return someclass and 'class="%s"' % someclass or '' + +class EPropertyPrimaryView(baseviews.PrimaryView): + __select__ = implements('EProperty') + skip_none = False + + class SystemEPropertiesForm(FormMixIn, StartupView): id = 'systemepropertiesform' - __select__ = none_rset & match_user_groups('managers') + __select__ = none_rset() & match_user_groups('managers') title = _('site configuration') controller = 'edit' @@ -66,6 +69,7 @@ return str('%s_property_%s' % (self.config.appid, somestr)) def _group_status(self, group, default=u'hidden'): + """return css class name 'hidden' (collapsed), or '' (open)""" cookies = self.req.get_cookie() cookiename = self._cookie_name(group) cookie = cookies.get(cookiename) @@ -97,19 +101,17 @@ mainopts.setdefault(parts[0], []).append(key) # precompute form to consume error message for group, keys in mainopts.items(): - mainopts[group] = self.form(keys, False) + mainopts[group] = self.form(keys, True) for group, objects in groupedopts.items(): for oid, keys in objects.items(): groupedopts[group][oid] = self.form(keys, True) - w = self.w req = self.req _ = req._ w(u'

%s

\n' % _(self.title)) - w(self.error_message()) for label, group, form in sorted((_(g), g, f) for g, f in mainopts.iteritems()): - status = css_class(self._group_status(group)) #'hidden' (collapsed), or '' (open) ? + status = css_class(self._group_status(group)) w(u'

%s

\n' % (make_togglable_link('fieldset_' + group, label, self._cookie_name(group)))) @@ -139,7 +141,8 @@ @property @cached def eprops_rset(self): - return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V, NOT P for_user U') + return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, ' + 'P value V, NOT P for_user U') @property def defined_keys(self): @@ -155,65 +158,38 @@ else: entity = self.vreg.etype_class('EProperty')(self.req, None, None) entity.eid = self.req.varmaker.next() + entity['pkey'] = key entity['value'] = self.vreg.property_value(key) return entity def form(self, keys, splitlabel=False): - stream = UStringIO() - w = stream.write - w(u'
\n' % self.build_url()) - w(u'
\n') - w(u'\n' - % html_escape(self.req.url())) - w(u'\n' % self.id) + buttons = [SubmitButton(), + Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')] + form = CompositeForm(self.req, domid=None, action=self.build_url(), + form_buttons=buttons, + submitmsg=self.req._('changes applied')) path = self.req.relative_path() if '?' in path: path, params = path.split('?', 1) - w(u'\n' - % html_escape(params)) - w(u'\n' % path) - #w(u'\n') - w(u'\n' - % self.req._('changes applied')) - w(u'
\n') + form.form_add_hidden('__redirectparams', params) + form.form_add_hidden('__redirectpath', path) + for key in keys: + self.form_row(form, key, splitlabel) + return form.form_render() - w(u'\n') - for key in keys: - w(u'\n') - self.form_row(w, key, splitlabel) - w(u'\n') - w(u'
\n') - w(u'
\n') - w(self.button_ok()) - w(self.button_cancel()) - w(u'
\n') - w(u'
\n') - w(u'
\n') - return stream.getvalue() - - def form_row(self, w, key, splitlabel): + def form_row(self, form, key, splitlabel): entity = self.entity_for_key(key) if splitlabel: - w(u'%s' % self.req._(key.split('.')[-1])) + label = key.split('.')[-1] else: - w(u'%s' % self.req._(key)) - wdg = self.vreg.property_value_widget(key, req=self.req) - error = wdg.render_error(entity) - w(u'' % (error and 'error' or '')) - w(error) - self.form_row_hiddens(w, entity, key) - w(wdg.edit_render(entity)) - w(u'\n') - w(u'%s' % wdg.render_help(entity)) - return entity - - def form_row_hiddens(self, w, entity, key): - eid = entity.eid - w(u'' % eid) - w(u'' % eid_param('__type', eid)) - w(u'' % (eid_param('pkey', eid), key)) - w(u'' % (eid_param('edits-pkey', eid), '')) - + label = key + subform = EntityFieldsForm(self.req, entity=entity, set_error_url=False) + subform.append_field(PropertyValueField(name='value', label=label, + eidparam=True)) + subform.vreg = self.vreg + subform.form_add_hidden('pkey', key, eidparam=True) + form.form_add_subform(subform) + return subform def is_user_prefs(cls, req, rset, row=None, col=0, **kwargs): @@ -225,8 +201,8 @@ __select__ = ( # we don't want guests to be able to come here match_user_groups('users', 'managers') & - (none_rset | ((one_line_rset() & is_user_prefs) & - (one_line_rset() & match_user_groups('managers')))) + (none_rset() | ((one_line_rset() & is_user_prefs) & + (one_line_rset() & match_user_groups('managers')))) ) title = _('preferences') @@ -243,14 +219,106 @@ return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V,' 'P for_user U, U eid %(x)s', {'x': self.user.eid}) - def form_row_hiddens(self, w, entity, key): - super(EPropertiesForm, self).form_row_hiddens(w, entity, key) + def form_row(self, form, key, splitlabel): + subform = super(EPropertiesForm, self).form_row(form, key, splitlabel) # if user is in the managers group and the property is being created, # we have to set for_user explicitly - if not entity.has_eid() and self.user.matching_groups('managers'): - eid = entity.eid - w(u'' - % (eid_param('edits-for_user', eid), INTERNAL_FIELD_VALUE)) - w(u'' - % (eid_param('for_user', eid), self.user.eid)) + if not subform.edited_entity.has_eid() and self.user.matching_groups('managers'): + subform.form_add_hidden('for_user', self.user.eid, eidparam=True) + + +# eproperty entity edition #################################################### + +class PropertyKeyField(StringField): + """specific field for EProperty.pkey to set the value widget according to + the selected key + """ + widget = Select + + def render(self, form, renderer): + wdg = self.get_widget(form) + wdg.attrs['tabindex'] = form.req.next_tabindex() + wdg.attrs['onchange'] = "javascript:setPropValueWidget('%s', %s)" % ( + form.edited_entity.eid, form.req.next_tabindex()) + return wdg.render(form, self) + + def vocabulary(self, form): + entity = form.edited_entity + _ = form.req._ + if entity.has_eid(): + return [(_(entity.pkey), entity.pkey)] + # key beginning with 'system.' should usually not be edited by hand + choices = entity.vreg.user_property_keys() + return sorted(zip((_(v) for v in choices), choices)) + + +class PropertyValueField(StringField): + """specific field for EProperty.value which will be different according to + the selected key type and vocabulary information + """ + def render(self, form, renderer=None, tabindex=None): + if not (form.edited_entity.has_eid() or 'pkey' in form.edited_entity): + # no key set yet, just include an empty div which will be filled + # on key selection + # empty span as well elsehtml validation fail (label is refering to + # this id) + domid = form.context[self]['id'] + return u'
' % (domid, domid) + wdg = self.get_widget(form) + if tabindex is not None: + wdg.attrs['tabindex'] = tabindex + return wdg.render(form, self) + def form_init(self, form): + entity = form.edited_entity + try: + pdef = form.vreg.property_info(entity.pkey) + except UnknownProperty, ex: + self.warning('%s (you should probably delete that property ' + 'from the database)', ex) + msg = form.req._('you should probably delete that property') + self.widget = NotEditableWidget(entity.printable_value('value'), + '%s (%s)' % (msg, ex)) + if entity.pkey.startswith('system.'): + msg = form.req._('value associated to this key is not editable ' + 'manually') + self.widget = NotEditableWidget(entity.printable_value('value'), msg) + # XXX race condition when used from EPropertyForm, should not rely on + # instance attributes + self.initial = pdef['default'] + self.help = pdef['help'] + vocab = pdef['vocabulary'] + if vocab is not None: + if callable(vocab): + # list() just in case its a generator function + self.choices = list(vocab(form.req)) + else: + self.choices = vocab + wdg = Select() + else: + wdg = FIELDS[pdef['type']].widget() + if pdef['type'] == 'Boolean': + self.choices = [(form.req._('yes'), '1'), (form.req._('no'), '')] + elif pdef['type'] in ('Float', 'Int'): + wdg.attrs.setdefault('size', 3) + self.widget = wdg + + +class NotEditableWidget(object): + def __init__(self, value, msg=None): + self.value = value + self.msg = msg + + def render(self, form, field): + domid = form.context[field]['id'] + value = '%s' % (domid, self.value) + if self.msg: + value + '
%s
' % self.msg + return value + + +class EPropertyForm(AutomaticEntityForm): + __select__ = entity_implements('EProperty') + pkey = PropertyKeyField(eidparam=True) + value = PropertyValueField(eidparam=True) +