web/views/editforms.py
author sylvain.thenault@logilab.fr
Thu, 12 Mar 2009 16:29:00 +0100
branchtls-sprint
changeset 1091 b5e253c0dd13
child 1132 96752791c2b6
permissions -rw-r--r--
a bit of reorganisation inside web/views: * move all workflow related stuff into views/workflow.py * move all schema related stuff into views/schema.py * move all RSS related stuff into views/xmlrss.py * start new editforms module, designed to contains new automatic forms code

"""Set of HTML automatic forms to create, delete, copy or edit a single entity
or a list of entities of the same type

:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"

from copy import copy

from simplejson import dumps

from logilab.mtconverter import html_escape
from logilab.common.decorators import cached

from cubicweb.selectors import (specified_etype_implements, implements,
                                match_kwargs, match_form_params, one_line_rset,
                                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
from cubicweb.web.form import MultipleFieldsForm, EntityFieldsForm, FormMixIn, FormRenderer
from cubicweb.web.formfields import StringField,  RichTextField, guess_field
from cubicweb.web.formwidgets import HiddenInput

_ = unicode


class DeleteConfForm(EntityView):
    id = 'deleteconf'
    title = _('delete')
    domid = 'deleteconf'
    onsubmit = None
    # don't use navigation, all entities asked to be deleted should be displayed
    # else we will only delete the displayed page
    need_navigation = False
    
    def call(self):
        """ask for confirmation before real deletion"""
        req, w = self.req, self.w
        _ = req._
        w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'
          % _('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 = MultipleFieldsForm(req, domid='deleteconf', action=self.build_url('edit'),
                                  onsubmit=self.onsubmit, copy_nav_params=True)
        form.buttons.append(form.button_delete(label=stdmsgs.YES))
        form.buttons.append(form.button_cancel(label=stdmsgs.NO))
        done = set()
        w(u'<ul>\n')
        for i in xrange(self.rset.rowcount):
            if self.rset[i][0] in done:
                continue
            done.add(self.rset[i][0])
            entity = self.rset.get_entity(i, 0)
            subform = EntityFieldsForm(req, set_error_url=False,
                                       entity=entity)
            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'),
                                      href=entity.absolute_url()))
        w(u'</ul>\n')
        w(form.form_render())


class ClickAndEditForm(FormMixIn, EntityView):
    id = 'reledit'
    __select__ = non_final_entity() & match_kwargs('rtype')

    # FIXME editableField class could be toggleable from userprefs
      
    def cell_call(self, row, col, rtype=None, role='subject', reload=False):
        entity = self.entity(row, col)
        if getattr(entity, rtype) is None:
            value = self.req._('not specified')
        else:
            value = entity.printable_value(rtype)
        if not entity.has_perm('update'):
            self.w(value)
            return
        self.req.add_js( ('cubicweb.ajax.js',) )
        eid = entity.eid
        edit_key = make_uid('%s-%s' % (rtype, eid))
        divid = 'd%s' % edit_key
        reload = dumps(reload)
        buttons = [tags.input(klass="validateButton", type="submit", name="__action_apply",
                              value=self.req._(stdmsgs.BUTTON_OK), tabindex=self.req.next_tabindex()),
                   tags.input(klass="validateButton", type="button",
                              value=self.req._(stdmsgs.BUTTON_CANCEL),
                              onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" % (eid, rtype, divid),
                              tabindex=self.req.next_tabindex())]
        form = self.vreg.select_object('forms', 'edition', self.req, self.rset, row=row, col=col,
                                       entity=entity, domid='%s-form' % divid, action='#',
                                       cssstyle='display: none', buttons=buttons,
                                       onsubmit="return inlineValidateForm('%(divid)s-form', '%(rtype)s', '%(eid)s', '%(divid)s', %(reload)s);" % locals())
        renderer = FormRenderer(display_label=False, display_help=False,
                                display_fields=(rtype,), button_bar_class='buttonbar')
        self.w(tags.div(value, klass='editableField', id=divid,
                        ondblclick="showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')" % locals()))
        self.w(form.render(renderer=renderer))


class AutomaticEntityForm(EntityFieldsForm):
    id = 'edition'
    needs_js = EntityFieldsForm.needs_js + ('cubicweb.ajax.js',)
    
    def __init__(self, *args, **kwargs):
        super(AutomaticEntityForm, self).__init__(*args, **kwargs)
        self.entity.complete()
        for rschema, target in self.editable_attributes(entity):
            field = guess_field(entity.__class__, entity.e_schema, rschema, target)
            self.fields.append(field)
            
    def form_buttons(self):
        return [self.button_ok(tabindex=self.req.next_tabindex()),
                self.button_apply(tabindex=self.req.next_tabindex()),
                self.button_cancel(tabindex=self.req.next_tabindex())]

    def editable_attributes(self, entity):
        # XXX both (add, delete) required for non final relations
        return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add')
                if rschema != 'eid']
    
class _EditionForm(EntityView):
    """primary entity edition form

    When generating a new attribute_input, the editor will look for a method
    named 'default_ATTRNAME' on the entity instance, where ATTRNAME is the
    name of the attribute being edited. You may use this feature to compute
    dynamic default values such as the 'tomorrow' date or the user's login
    being connected
    """    
    id = 'edition'
    __select__ = one_line_rset() & non_final_entity()

    title = _('edition')
    controller = 'edit'
    skip_relations = FormMixIn.skip_relations.copy()

    def cell_call(self, row, col, **kwargs):
        self.req.add_js( ('cubicweb.ajax.js',) )
        self.initialize_varmaker()
        entity = self.complete_entity(row, col)

    def initialize_varmaker(self):
        varmaker = self.req.get_page_data('rql_varmaker')
        if varmaker is None:
            varmaker = self.req.varmaker
            self.req.set_page_data('rql_varmaker', varmaker)
        self.varmaker = varmaker
        
    def edit_form(self, entity, kwargs):
        form = EntityFieldsForm(self.req, entity=entity)
        for rschema, target in self.editable_attributes(entity):
            field = guess_field(entity.__class__, entity.e_schema, rschema, target)
            form.fields.append(field)
        form.buttons.append(form.button_ok())
        form.buttons.append(form.button_apply())
        form.buttons.append(form.button_cancel())
        self.w(form.form_render())

    def editable_attributes(self, entity):
        # XXX both (add, delete)
        return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add')
                if rschema != 'eid']