示例#1
0
class RelationBox(component.CtxComponent):
    """ Helper view class to display a relation rset in a sidebox.
    """
    __select__ = nonempty_rset() & match_kwargs("title", "rql")
    __regid__ = "relationbox"
    cw_property_defs = {}
    context = "incontext"

    @property
    def domid(self):
        return (super(RelationBox, self).domid + unicode(abs(id(self))) +
                unicode(abs(id(self.cw_rset))))

    def render_title(self, w):
        w(self.cw_extra_kwargs["title"])

    def render_body(self, w):
        defaultlimit = self._cw.property_value("navigation.related-limit")
        if not isinstance(self.cw_rset, list):
            rset = list(self.cw_rset.entities())
        else:
            rset = self.cw_rset
        for entity in rset[:(defaultlimit - 1)]:
            w(u"<div>&bull; " + entity.view(self.context) + u"</div>")
        # if len(rset) == defaultlimit:
        rql = self.cw_extra_kwargs["rql"]
        href = self._cw.build_url(rql=rql)
        w(u"<br/><div><a href='{0}'>&#8634; see more</a></div>".format(href))
示例#2
0
class InlineAddNewLinkView(InlineEntityCreationFormView):
    """
    :attr card: the cardinality of the relation according to role of `peid`
    """
    __regid__ = 'inline-addnew-link'
    __select__ = (match_kwargs('peid', 'petype', 'rtype')
                  & specified_etype_implements('Any'))

    _select_attrs = InlineEntityCreationFormView._select_attrs + ('card', )
    card = None  # make pylint happy
    form = None  # no actual form wrapped

    def call(self, i18nctx, **kwargs):
        self._cw.set_varmaker()
        divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role,
                                     self.peid)
        self.w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">' %
               divid)
        js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s', '%s')" % (
            self.peid, self.petype, self.etype, self.rtype, self.role, i18nctx)
        if self.pform.should_hide_add_new_relation_link(self.rtype, self.card):
            js = "toggleVisibility('%s'); %s" % (divid, js)
        __ = self._cw.pgettext
        self.w(
            u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
            %
            (self.rtype, self.peid, js, __(i18nctx, 'add a %s' % self.etype)))
        self.w(u'</div>')
示例#3
0
class GroupByView(EntityView):
    """grouped view of a result set. The `group_key` method return the group
    key of an entities (a string or tuple of string).

    For each group, display a link to entities of this group by generating url
    like <basepath>/<key> or <basepath>/<key item 1>/<key item 2>.
    """
    __abstract__ = True
    __select__ = EntityView.__select__ & match_kwargs('basepath')
    entity_attribute = None
    reversed = False

    def index_url(self, basepath, key, **kwargs):
        if isinstance(key, (list, tuple)):
            key = '/'.join(key)
        return self._cw.build_url('%s/%s' % (basepath, key), **kwargs)

    def index_link(self, basepath, key, items):
        url = self.index_url(basepath, key)
        if isinstance(key, (list, tuple)):
            key = ' '.join(key)
        return tags.a(key, href=url)

    def group_key(self, entity, **kwargs):
        value = getattr(entity, self.entity_attribute)
        if callable(value):
            value = value()
        return value

    def call(self, basepath, maxentries=None, **kwargs):
        index = {}
        for entity in self.cw_rset.entities():
            index.setdefault(self.group_key(entity, **kwargs),
                             []).append(entity)
        displayed = sorted(index)
        if self.reversed:
            displayed = reversed(displayed)
        if maxentries is None:
            needmore = False
        else:
            needmore = len(index) > maxentries
            displayed = tuple(displayed)[:maxentries]
        w = self.w
        w(u'<ul class="boxListing">')
        for key in displayed:
            if key:
                w(u'<li>%s</li>\n' %
                  self.index_link(basepath, key, index[key]))
        if needmore:
            url = self._cw.build_url('view',
                                     vid=self.__regid__,
                                     rql=self.cw_rset.printable_rql())
            w(u'<li>%s</li>\n' %
              tags.a(u'[%s]' % self._cw._('see more'), href=url))
        w(u'</ul>\n')
示例#4
0
class UndoableTransactionView(View):
    __regid__ = 'undoable-transaction-view'
    __select__ = View.__select__ & match_kwargs('txuuid')

    item_vid = 'undoable-action-list-view'
    cache_max_age = 0

    def build_undo_link(self,
                        txuuid,
                        redirect_path=None,
                        redirect_params=None):
        """ the kwargs are passed to build_url"""
        _ = self._cw._
        redirect = {}
        if redirect_path:
            redirect['__redirectpath'] = redirect_path
        if redirect_params:
            if isinstance(redirect_params, dict):
                redirect['__redirectparams'] = self._cw.build_url_params(
                    **redirect_params)
            else:
                redirect['__redirectparams'] = redirect_params
        link_url = self._cw.build_url('undo', txuuid=txuuid, **redirect)
        msg = u"<span class='undo'>%s</span>" % tags.a(_('undo'),
                                                       href=link_url)
        return msg

    def call(self,
             txuuid,
             public=True,
             redirect_path=None,
             redirect_params=None):
        _ = self._cw._
        txinfo = self._cw.cnx.transaction_info(txuuid)
        try:
            #XXX Under some unknown circumstances txinfo.user_eid=-1
            user = self._cw.entity_from_eid(txinfo.user_eid)
        except UnknownEid:
            user = None
        undo_url = self.build_undo_link(txuuid,
                                        redirect_path=redirect_path,
                                        redirect_params=redirect_params)
        txinfo_dict = dict(dt=self._cw.format_date(txinfo.datetime, time=True),
                           user_eid=txinfo.user_eid,
                           user=user and user.view('outofcontext')
                           or _("undefined user"),
                           txuuid=txuuid,
                           undo_link=undo_url)
        self.w(_("By %(user)s on %(dt)s [%(undo_link)s]") % txinfo_dict)

        tx_actions = txinfo.actions_list(public=public)
        if tx_actions:
            self.wview(self.item_vid, None, tx_actions=tx_actions)
示例#5
0
class PyValListView(View):
    """display a list of values into an html list.

    Take care, content is NOT xml-escaped.
    """
    __regid__ = 'pyvallist'
    __select__ = match_kwargs('pyvalue')

    def call(self, pyvalue):
        self.w(u'<ul>\n')
        for line in pyvalue:
            self.w(u'<li>%s</li>\n' % line)
        self.w(u'</ul>\n')
示例#6
0
class URLAttributeView(EntityView):
    """:__regid__: *urlattr*

    This view will wrap an attribute value (hence expect a string) into an '<a>'
    HTML tag to display a clickable link.
    """
    __regid__ = 'urlattr'
    __select__ = EntityView.__select__ & match_kwargs('rtype')

    def entity_call(self, entity, rtype, **kwargs):
        url = entity.printable_value(rtype)
        if url:
            self.w(u'<a href="%s">%s</a>' % (url, url))
示例#7
0
class AddRelationView(component.EditRelationMixIn, View):
    """base class for view which let add entities linked by a given relation

    subclasses should define at least id, rtype and target class attributes.
    """
    __registry__ = 'views'
    __regid__ = 'xaddrelation'
    __select__ = (match_form_params('rtype', 'target')
                  | match_kwargs('rtype', 'target'))
    cw_property_defs = {}  # don't want to inherit this from Box
    expected_kwargs = form_params = ('rtype', 'target')

    def cell_call(self, row, col, rtype=None, target=None, etype=None):
        self.rtype = rtype or self._cw.form['rtype']
        self.target = target or self._cw.form['target']
        self.etype = etype or self._cw.form.get('etype')
        entity = self.cw_rset.get_entity(row, col)
        rschema = self._cw.vreg.schema.rschema(self.rtype)
        if not self.etype:
            if self.target == 'object':
                etypes = rschema.objects(entity.e_schema)
            else:
                etypes = rschema.subjects(entity.e_schema)
            if len(etypes) == 1:
                self.etype = etypes[0]
        self.w(u'<div id="%s">' % self.domid)
        self.w(
            u'<h1>%s</h1>' % self._cw._('relation %(relname)s of %(ent)s') % {
                'relname': rschema.display_name(self._cw, role(self)),
                'ent': entity.view('incontext')
            })
        self.w(u'<ul class="list-unstyled">')
        for boxitem in self.unrelated_boxitems(entity):
            self.w('<li>%s</li>' % boxitem)
        self.w(u'</ul></div>')

    def unrelated_entities(self, entity):
        """returns the list of unrelated entities

        if etype is not defined on the Box's class, the default
        behaviour is to use the entity's appropraite vocabulary function
        """
        # use entity.unrelated if we've been asked for a particular etype
        if getattr(self, 'etype', None):
            rset = entity.unrelated(self.rtype,
                                    self.etype,
                                    role(self),
                                    ordermethod='fetch_order')
            self.paginate(self._cw, rset=rset, w=self.w)
            return rset.entities()
        super(AddRelationView, self).unrelated_entities(self)
示例#8
0
class VerbatimAttributeView(EntityView):
    """:__regid__: *verbatimattr*

    This view will wrap an attribute value into an '<pre>' HTML tag to display
    arbitrary text where EOL will be respected. It usually make sense for
    attributes whose value is a multi-lines string where new lines matters.
    """
    __regid__ = 'verbatimattr'
    __select__ = EntityView.__select__ & match_kwargs('rtype')

    def entity_call(self, entity, rtype, **kwargs):
        value = entity.printable_value(rtype)
        if value:
            self.w(u'<pre>%s</pre>' % value)
示例#9
0
class DataFeedSourceDataImport(EntityView):
    __select__ = EntityView.__select__ & match_kwargs('rtype')
    __regid__ = 'cw.formated_log'

    def cell_call(self, row, col, rtype, loglevel='Info', **kwargs):
        if 'dispctrl' in self.cw_extra_kwargs:
            loglevel = self.cw_extra_kwargs['dispctrl'].get(
                'loglevel', loglevel)
        entity = self.cw_rset.get_entity(row, col)
        value = getattr(entity, rtype)
        if value:
            self._cw.view('cw.log.table',
                          pyvalue=log_to_table(self._cw, value),
                          default_level=loglevel,
                          w=self.w)
        else:
            self.w(self._cw._('no log to display'))
示例#10
0
class UndoableActionListView(View):
    __regid__ = 'undoable-action-list-view'
    __select__ = View.__select__ & match_kwargs('tx_actions')
    title = _('Undoable actions')
    item_vid = 'undoable-action-view'
    cache_max_age = 0

    def call(self, tx_actions):
        if tx_actions :
            self.w(u"<ol class='undo-actions'>")
            for action in tx_actions:
                self.cell_call(action)
            self.w(u"</ol>")

    def cell_call(self, action):
        self.w(u'<li>')
        self.wview(self.item_vid, None, tx_action=action)
        self.w(u'</li>\n')
示例#11
0
class AttributeView(EntityView):
    """:__regid__: *attribute*

    This view is generally used to disable the *reledit* feature. It works on
    both relations and attributes.
    """
    __regid__ = 'attribute'
    __select__ = EntityView.__select__ & match_kwargs('rtype')

    def entity_call(self, entity, rtype, role='subject', **kwargs):
        if self._cw.vreg.schema.rschema(rtype).final:
            self.w(entity.printable_value(rtype))
        else:
            dispctrl = uicfg.primaryview_display_ctrl.etype_get(
                entity.e_schema, rtype, role, '*')
            rset = entity.related(rtype, role)
            if rset:
                self.wview('autolimited', rset, initargs={'dispctrl': dispctrl})
示例#12
0
class InlineEntityCreationFormView(InlineEntityEditionFormView):
    """
    :attr etype: the entity type being created in the inline form
    """
    __regid__ = 'inline-creation'
    __select__ = (match_kwargs('peid', 'petype', 'rtype')
                  & specified_etype_implements('Any'))
    _select_attrs = InlineEntityEditionFormView._select_attrs + ('petype', )

    # make pylint happy
    petype = None

    @property
    def removejs(self):
        entity = self._entity()
        rdef = entity.e_schema.rdef(self.rtype, neg_role(self.role),
                                    self.petype)
        card = rdef.role_cardinality(self.role)
        # when one is adding an inline entity for a relation of a single card,
        # the 'add a new xxx' link disappears. If the user then cancel the addition,
        # we have to make this link appears back. This is done by giving add new link
        # id to removeInlineForm.
        if card == '?':
            divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role,
                                         self.peid)
            return "removeInlineForm('%%s', '%%s', '%s', '%%s', '%s')" % (
                self.role, divid)
        elif card in '+*':
            return "removeInlineForm('%%s', '%%s', '%s', '%%s')" % self.role
        # don't do anything for card == '1'

    @cached
    def _entity(self):
        try:
            cls = self._cw.vreg['etypes'].etype_class(self.etype)
        except Exception:
            self.w(self._cw._('no such entity type %s') % self.etype)
            return
        entity = cls(self._cw)
        entity.eid = next(self._cw.varmaker)
        return entity

    def call(self, i18nctx, **kwargs):
        self.render_form(i18nctx, **kwargs)
示例#13
0
class OneLineSelectableView(EntityView):
    """custom oneline view used by company / division treeview"""
    __regid__ = 'oneline-selectable'
    __select__ = is_instance('Company') & match_kwargs('onscreen')

    def cell_call(self, row, col, onscreen):
        entity = self.cw_rset.get_entity(row, col)
        if entity.eid == onscreen:
            self.w(u'<span class="currentCompany">%s</span>'
                   % xml_escape(entity.view('textincontext')))
        else:
            if entity.headquarters:
                tooltip = xml_escape(entity.headquarters[0].dc_long_title())
            else:
                tooltip = u''
            self.w(u'<a href="%s" title="%s">%s</a>'
                   % (xml_escape(entity.absolute_url()),
                      xml_escape(tooltip),
                      xml_escape(entity.dc_title())))
示例#14
0
class RegisterUserService(Service):
    """check if a user with the given login exists, if not create it with the
    given password. This service is designed to be used for anonymous
    registration on public web sites.

    To use it, do:
     with self.appli.repo.internal_cnx() as cnx:
        cnx.call_service('register_user',
                         login=login,
                         password=password,
                         **cwuserkwargs)
    """
    __regid__ = 'register_user'
    __select__ = Service.__select__ & match_kwargs('login', 'password')
    default_groups = ('users', )

    def call(self, login, password, email=None, groups=None, **cwuserkwargs):
        cnx = self._cw
        if isinstance(password, str):
            # password should *always* be utf8 encoded
            password = password.encode('UTF8')
        cwuserkwargs['login'] = login
        cwuserkwargs['upassword'] = password
        # we have to create the user
        user = cnx.create_entity('CWUser', **cwuserkwargs)
        if groups is None:
            groups = self.default_groups
        assert groups, "CWUsers must belong to at least one CWGroup"
        group_names = ', '.join('%r' % group for group in groups)
        cnx.execute(
            'SET X in_group G WHERE X eid %%(x)s, G name IN (%s)' %
            group_names, {'x': user.eid})
        if email or '@' in login:
            d = {'login': login, 'email': email or login}
            cnx.execute(
                'INSERT EmailAddress X: X address %(email)s, '
                'U primary_email X, U use_email X '
                'WHERE U login %(login)s',
                d,
                build_descr=False)

        return user
示例#15
0
class RsetBox(component.CtxComponent):
    """helper view class to display an rset in a sidebox"""
    __select__ = nonempty_rset() & match_kwargs('title', 'vid')
    __regid__ = 'rsetbox'
    cw_property_defs = {}
    context = 'incontext'

    @property
    def domid(self):
        return super(RsetBox, self).domid + str(abs(id(self))) + str(abs(id(self.cw_rset)))

    def render_title(self, w):
        w(self.cw_extra_kwargs['title'])

    def render_body(self, w):
        if 'dispctrl' in self.cw_extra_kwargs:
            # XXX do not modify dispctrl!
            self.cw_extra_kwargs['dispctrl'].setdefault('subvid', 'outofcontext')
            self.cw_extra_kwargs['dispctrl'].setdefault('use_list_limit', 1)
        self._cw.view(self.cw_extra_kwargs['vid'], self.cw_rset, w=w,
                      initargs=self.cw_extra_kwargs)
示例#16
0
class LogFormView(View):
    # XXX an awful lot of hardcoded assumptions there
    #     makes it unobvious to reuse/specialize
    __regid__ = 'logform'
    __select__ = match_kwargs('id', 'klass')

    title = 'log in'

    def call(self, id, klass, title=True, showmessage=True):
        w = self.w
        w(u'<div id="%s" class="%s">' % (id, klass))
        if title:
            stitle = self._cw.property_value('ui.site-title')
            if stitle:
                stitle = xml_escape(stitle)
            else:
                stitle = u'&#160;'
            w(u'<div class="loginTitle">%s</div>' % stitle)
        w(u'<div class="loginContent">\n')
        # don't call self._cw.message twice since it pops the id
        message = self._cw.message
        if showmessage and message:
            w(u'<div class="loginMessage">%s</div>\n' % message)
        config = self._cw.vreg.config
        if config['auth-mode'] != 'http':
            self.login_form(id)  # Cookie authentication
        w(u'</div>')
        w(u'</div>\n')

    def login_form(self, id):
        cw = self._cw
        form = cw.vreg['forms'].select('logform', cw)
        if cw.vreg.config['allow-email-login']:
            label = cw._('login or email')
        else:
            label = cw.pgettext('CWUser', 'login')
        form.field_by_name('__login').label = label
        form.render(w=self.w, table_class='', display_progress_div=False)
        cw.html_headers.add_onload('jQuery("#__login:visible").focus()')
示例#17
0
 def test_match_kwargs_any(self):
     selector = match_kwargs( set( ('a', 'b') ), mode='any')
     self.assertEqual(selector(None, None, a=1, b=2), 2)
     self.assertEqual(selector(None, None, a=1), 1)
     self.assertEqual(selector(None, None, c=1), 0)
     self.assertEqual(selector(None, None, a=1, c=1), 1)
示例#18
0
 def test_match_kwargs_default(self):
     selector = match_kwargs( set( ('a', 'b') ) )
     self.assertEqual(selector(None, None, a=1, b=2), 2)
     self.assertEqual(selector(None, None, a=1), 0)
     self.assertEqual(selector(None, None, c=1), 0)
     self.assertEqual(selector(None, None, a=1, c=1), 0)
示例#19
0
class RsetTableView(TableMixIn, AnyRsetView):
    """This table view accepts any non-empty rset. It uses introspection on the
    result set to compute column names and the proper way to display the cells.

    It is highly configurable and accepts a wealth of options, but take care to
    check what you're trying to achieve wouldn't be a job for the
    :class:`EntityTableView`. Basically the question is: does this view should
    be tied to the result set query's shape or no? If yes, than you're fine. If
    no, you should take a look at the other table implementation.

    The following class attributes may be used to control the table:

    * `finalvid`, a view identifier that should be called on final entities
      (e.g. attribute values). Default to 'final'.

    * `nonfinalvid`, a view identifier that should be called on
      entities. Default to 'incontext'.

    * `displaycols`, if not `None`, should be a list of rset's columns to be
      displayed.

    * `headers`, if not `None`, should be a list of headers for the table's
      columns.  `None` values in the list will be replaced by computed column
      names.

    * `cellvids`, if not `None`, should be a dictionary with table column index
      as key and a view identifier as value, telling the view that should be
      used in the given column.

    Notice `displaycols`, `headers` and `cellvids` may be specified at selection
    time but then the table won't have pagination and shouldn't be configured to
    display the facets filter nor actions (as they wouldn't behave as expected).

    This table class use the :class:`RsetTableColRenderer` as default column
    renderer.

    .. autoclass:: RsetTableColRenderer
    """    #'# make emacs happier
    __regid__ = 'table'
    # selector trick for bw compath with the former :class:TableView
    __select__ = AnyRsetView.__select__ & (~match_kwargs('title',
                                                         'subvid',
                                                         'displayfilter',
                                                         'headers',
                                                         'displaycols',
                                                         'displayactions',
                                                         'actions',
                                                         'divid',
                                                         'cellvids',
                                                         'cellattrs',
                                                         'mainindex',
                                                         'paginate',
                                                         'page_size',
                                                         mode='any')
                                           | unreloadable_table())
    title = _('table')
    # additional configuration parameters
    finalvid = 'final'
    nonfinalvid = 'incontext'
    displaycols = None
    headers = None
    cellvids = None
    default_column_renderer_class = RsetTableColRenderer

    def linkable(self):
        # specific subclasses of this view usually don't want to be linkable
        # since they depends on a particular shape (being linkable meaning view
        # may be listed in possible views
        return self.__regid__ == 'table'

    def call(self,
             headers=None,
             displaycols=None,
             cellvids=None,
             paginate=None):
        if self.headers:
            self.headers = [h and self._cw._(h) for h in self.headers]
        if (headers or displaycols or cellvids or paginate):
            if headers is not None:
                self.headers = headers
            if displaycols is not None:
                self.displaycols = displaycols
            if cellvids is not None:
                self.cellvids = cellvids
            if paginate is not None:
                self.paginable = paginate
        super(RsetTableView, self).call()

    def main_var_index(self):
        """returns the index of the first non-attribute variable among the RQL
        selected variables
        """
        eschema = self._cw.vreg.schema.eschema
        for i, etype in enumerate(self.cw_rset.description[0]):
            if not eschema(etype).final:
                return i
        return None

    # layout callbacks #########################################################

    @property
    def table_size(self):
        """return the number of rows (header excluded) to be displayed"""
        return self.cw_rset.rowcount

    def build_column_renderers(self):
        headers = self.headers
        # compute displayed columns
        if self.displaycols is None:
            if headers is not None:
                displaycols = list(range(len(headers)))
            else:
                rqlst = self.cw_rset.syntax_tree()
                displaycols = list(range(len(rqlst.children[0].selection)))
        else:
            displaycols = self.displaycols
        # compute table headers
        main_var_index = self.main_var_index()
        computed_titles = self.columns_labels(main_var_index)
        # compute build renderers
        cellvids = self.cellvids
        renderers = []
        for colnum, colid in enumerate(displaycols):
            addcount = False
            # compute column header
            title = None
            if headers is not None:
                title = headers[colnum]
            if title is None:
                title = computed_titles[colid]
            if colid == main_var_index:
                addcount = True
            # compute cell vid for the column
            if cellvids is not None and colnum in cellvids:
                cellvid = cellvids[colnum]
            else:
                coltype = self.cw_rset.description[0][colid]
                if coltype is not None and self._cw.vreg.schema.eschema(
                        coltype).final:
                    cellvid = self.finalvid
                else:
                    cellvid = self.nonfinalvid
            # get renderer
            renderer = self.column_renderer(colid,
                                            header=title,
                                            trheader=False,
                                            addcount=addcount,
                                            cellvid=cellvid)
            renderers.append(renderer)
        return renderers
示例#20
0
class PyValTableView(tableview.TableMixIn, View):
    """This table view is designed to be used a list of list of unicode values
    given as a mandatory `pyvalue` argument. Take care, content is NOT
    xml-escaped.

    It's configured through the following selection arguments.

    If `headers` is specified, it is expected to be a list of headers to be
    inserted as first row (in <thead>).

    `header_column_idx` may be used to specify a column index or a set of column
    indiced where values should be inserted inside <th> tag instead of <td>.

    `cssclass` is the CSS class used on the <table> tag, and default to
    'listing' (so that the table will look similar to those generated by the
    table view).
    """
    __regid__ = 'pyvaltable'
    __select__ = match_kwargs('pyvalue')
    default_column_renderer_class = PyValTableColRenderer
    paginable = False  # not supported
    headers = None
    cssclass = None
    domid = None

    def __init__(self,
                 req,
                 pyvalue,
                 headers=None,
                 cssclass=None,
                 header_column_idx=None,
                 **kwargs):
        super(PyValTableView, self).__init__(req, **kwargs)
        self.pyvalue = pyvalue
        if headers is not None:
            self.headers = headers
        elif self.headers:  # headers set on a class attribute, translate
            self.headers = [self._cw._(header) for header in self.headers]
        if cssclass is not None:
            self.cssclass = cssclass
        self.header_column_idx = header_column_idx

    @property
    def layout_args(self):
        args = {}
        if self.cssclass:
            args['cssclass'] = self.cssclass
        if self.header_column_idx is not None:
            args['header_column_idx'] = self.header_column_idx
        return args

    # layout callbacks #########################################################

    @property
    def table_size(self):
        """return the number of rows (header excluded) to be displayed"""
        return len(self.pyvalue)

    @property
    def has_headers(self):
        return self.headers

    def build_column_renderers(self):
        return [
            self.column_renderer(colid)
            for colid in range(len(self.pyvalue[0]))
        ]

    def facets_form(self, mainvar=None):
        return None  # not supported

    def table_actions(self):
        return []  # not supported
示例#21
0
class InlineEntityEditionFormView(f.FormViewMixIn, EntityView):
    """
    :attr peid: the parent entity's eid hosting the inline form
    :attr rtype: the relation bridging `etype` and `peid`
    :attr role: the role played by the `peid` in the relation
    :attr pform: the parent form where this inlined form is being displayed
    """
    __regid__ = 'inline-edition'
    __select__ = non_final_entity() & match_kwargs('peid', 'rtype')

    _select_attrs = ('peid', 'rtype', 'role', 'pform', 'etype')
    removejs = "removeInlinedEntity('%s', '%s', '%s')"
    form_renderer_id = 'inline'

    # make pylint happy
    peid = rtype = role = pform = etype = None

    def __init__(self, *args, **kwargs):
        for attr in self._select_attrs:
            # don't pop attributes from kwargs, so the end-up in
            # self.cw_extra_kwargs which is then passed to the edition form (see
            # the .form method)
            setattr(self, attr, kwargs.get(attr))
        super(InlineEntityEditionFormView, self).__init__(*args, **kwargs)

    def _entity(self):
        assert self.cw_row is not None, self
        return self.cw_rset.get_entity(self.cw_row, self.cw_col)

    @property
    def petype(self):
        assert isinstance(self.peid, int)
        pentity = self._cw.entity_from_eid(self.peid)
        return pentity.e_schema.type

    @property
    @cached
    def form(self):
        entity = self._entity()
        form = self._cw.vreg['forms'].select(
            'edition',
            self._cw,
            entity=entity,
            formtype='inlined',
            form_renderer_id=self.form_renderer_id,
            copy_nav_params=False,
            mainform=False,
            parent_form=self.pform,
            **self.cw_extra_kwargs)
        if self.pform is None:
            form.restore_previous_post(form.session_key())
        # assert form.parent_form
        self.add_hiddens(form, entity)
        return form

    def cell_call(self, row, col, i18nctx, **kwargs):
        """
        :param peid: the parent entity's eid hosting the inline form
        :param rtype: the relation bridging `etype` and `peid`
        :param role: the role played by the `peid` in the relation
        """
        entity = self._entity()
        divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (
            self.peid, self.rtype, entity.eid)
        self.render_form(i18nctx, divonclick=divonclick, **kwargs)

    def _get_removejs(self):
        """
        Don't display the remove link in edition form if the
        cardinality is 1. Handled in InlineEntityCreationFormView for
        creation form.
        """
        entity = self._entity()
        rdef = entity.e_schema.rdef(self.rtype, neg_role(self.role),
                                    self.petype)
        card = rdef.role_cardinality(self.role)
        if card == '1':  # don't display remove link
            return None
        # if cardinality is 1..n (+), dont display link to remove an inlined form for the first form
        # allowing to edit the relation. To detect so:
        #
        # * if parent form (pform) is None, we're generated through an ajax call and so we know this
        #   is not the first form
        #
        # * if parent form is not None, look for previous InlinedFormField in the parent's form
        #   fields
        if card == '+' and self.pform is not None:
            # retrieve all field'views handling this relation and return None if we're the first of
            # them
            first_view = next(
                iter((f.view for f in self.pform.fields
                      if isinstance(f, InlinedFormField) and f.view.rtype ==
                      self.rtype and f.view.role == self.role)))
            if self == first_view:
                return None
        return self.removejs and self.removejs % (self.peid, self.rtype,
                                                  entity.eid)

    def render_form(self, i18nctx, **kwargs):
        """fetch and render the form"""
        entity = self._entity()
        divid = '%s-%s-%s' % (self.peid, self.rtype, entity.eid)
        title = self.form_title(entity, i18nctx)
        removejs = self._get_removejs()
        countkey = '%s_count' % self.rtype
        try:
            self._cw.data[countkey] += 1
        except KeyError:
            self._cw.data[countkey] = 1
        self.form.render(w=self.w,
                         divid=divid,
                         title=title,
                         removejs=removejs,
                         i18nctx=i18nctx,
                         counter=self._cw.data[countkey],
                         **kwargs)

    def form_title(self, entity, i18nctx):
        return self._cw.pgettext(i18nctx, entity.cw_etype)

    def add_hiddens(self, form, entity):
        """to ease overriding (see cubes.vcsfile.views.forms for instance)"""
        iid = 'rel-%s-%s-%s' % (self.peid, self.rtype, entity.eid)
        #  * str(self.rtype) in case it's a schema object
        #  * neged_role() since role is the for parent entity, we want the role
        #    of the inlined entity
        form.add_hidden(name=str(self.rtype),
                        value=self.peid,
                        role=neg_role(self.role),
                        eidparam=True,
                        id=iid)

    def keep_entity(self, form, entity):
        if not entity.has_eid():
            return True
        # are we regenerating form because of a validation error?
        if form.form_previous_values:
            cdvalues = self._cw.list_form_param(
                eid_param(self.rtype, self.peid), form.form_previous_values)
            if str(entity.eid) not in cdvalues:
                return False
        return True
示例#22
0
class EntityFieldsForm(FieldsForm):
    """This class is designed for forms used to edit some entities. It should
    handle for you all the underlying stuff necessary to properly work with the
    generic :class:`~cubicweb.web.views.editcontroller.EditController`.
    """

    __regid__ = 'base'
    __select__ = (match_kwargs('entity')
                  | (one_line_rset() & non_final_entity()))
    domid = 'entityForm'
    uicfg_aff = uicfg.autoform_field
    uicfg_affk = uicfg.autoform_field_kwargs

    @iclassmethod
    def field_by_name(cls_or_self, name, role=None, eschema=None):
        """return field with the given name and role. If field is not explicitly
        defined for the form but `eclass` is specified, guess_field will be
        called.
        """
        try:
            return super(EntityFieldsForm,
                         cls_or_self).field_by_name(name, role)
        except form.FieldNotFound:
            if eschema is None or role is None or name not in eschema.schema:
                raise
            rschema = eschema.schema.rschema(name)
            # XXX use a sample target type. Document this.
            tschemas = rschema.targets(eschema, role)
            fieldclass = cls_or_self.uicfg_aff.etype_get(
                eschema, rschema, role, tschemas[0])
            kwargs = cls_or_self.uicfg_affk.etype_get(eschema, rschema, role,
                                                      tschemas[0])
            if kwargs is None:
                kwargs = {}
            if fieldclass:
                if not isinstance(fieldclass, type):
                    return fieldclass  # already an instance
                kwargs['fieldclass'] = fieldclass
            if isinstance(cls_or_self, type):
                req = None
            else:
                req = cls_or_self._cw
            field = guess_field(eschema,
                                rschema,
                                role,
                                req=req,
                                eidparam=True,
                                **kwargs)
            if field is None:
                raise
            return field

    def __init__(self, _cw, rset=None, row=None, col=None, **kwargs):
        try:
            self.edited_entity = kwargs.pop('entity')
        except KeyError:
            self.edited_entity = rset.complete_entity(row or 0, col or 0)
        msg = kwargs.pop('submitmsg', None)
        super(EntityFieldsForm, self).__init__(_cw, rset, row, col, **kwargs)
        self.uicfg_aff = self._cw.vreg['uicfg'].select(
            'autoform_field', self._cw, entity=self.edited_entity)
        self.uicfg_affk = self._cw.vreg['uicfg'].select(
            'autoform_field_kwargs', self._cw, entity=self.edited_entity)
        self.add_hidden('__type', self.edited_entity.cw_etype, eidparam=True)

        self.add_hidden('eid', self.edited_entity.eid)
        self.add_generation_time()
        # mainform default to true in parent, hence default to True
        if kwargs.get('mainform', True) or kwargs.get('mainentity', False):
            self.add_hidden(u'__maineid', self.edited_entity.eid)
            # If we need to directly attach the new object to another one
            if '__linkto' in self._cw.form:
                if msg:
                    msg = '%s %s' % (msg, self._cw._('and linked'))
                else:
                    msg = self._cw._('entity linked')
        if msg:
            msgid = self._cw.set_redirect_message(msg)
            self.add_hidden('_cwmsgid', msgid)

    def add_generation_time(self):
        # use %f to prevent (unlikely) display in exponential format
        self.add_hidden('__form_generation_time',
                        '%.6f' % time.time(),
                        eidparam=True)

    def add_linkto_hidden(self):
        """add the __linkto hidden field used to directly attach the new object
        to an existing other one when the relation between those two is not
        already present in the form.

        Warning: this method must be called only when all form fields are setup
        """
        for (rtype, role), eids in self.linked_to.items():
            # if the relation is already setup by a form field, do not add it
            # in a __linkto hidden to avoid setting it twice in the controller
            try:
                self.field_by_name(rtype, role)
            except form.FieldNotFound:
                for eid in eids:
                    self.add_hidden('__linkto',
                                    '%s:%s:%s' % (rtype, eid, role))

    def render(self, *args, **kwargs):
        self.add_linkto_hidden()
        return super(EntityFieldsForm, self).render(*args, **kwargs)

    @property
    @cached
    def linked_to(self):
        linked_to = {}
        # case where this is an embeded creation form
        try:
            eid = int(self.cw_extra_kwargs['peid'])
        except (KeyError, ValueError):
            # When parent is being created, its eid is not numeric (e.g. 'A')
            # hence ValueError.
            pass
        else:
            ltrtype = self.cw_extra_kwargs['rtype']
            ltrole = neg_role(self.cw_extra_kwargs['role'])
            linked_to[(ltrtype, ltrole)] = [eid]
        # now consider __linkto if the current form is the main form
        try:
            self.field_by_name('__maineid')
        except form.FieldNotFound:
            return linked_to
        for linkto in self._cw.list_form_param('__linkto'):
            ltrtype, eid, ltrole = linkto.split(':')
            linked_to.setdefault((ltrtype, ltrole), []).append(int(eid))
        return linked_to

    def session_key(self):
        """return the key that may be used to store / retreive data about a
        previous post which failed because of a validation error
        """
        if self.force_session_key is not None:
            return self.force_session_key
        # XXX if this is a json request, suppose we should redirect to the
        # entity primary view
        if self._cw.ajax_request and self.edited_entity.has_eid():
            return '%s#%s' % (self.edited_entity.absolute_url(), self.domid)
        # XXX we should not consider some url parameters that may lead to
        # different url after a validation error
        return '%s#%s' % (self._cw.url(), self.domid)

    def default_renderer(self):
        return self._cw.vreg['formrenderers'].select(self.form_renderer_id,
                                                     self._cw,
                                                     rset=self.cw_rset,
                                                     row=self.cw_row,
                                                     col=self.cw_col,
                                                     entity=self.edited_entity)

    def should_display_add_new_relation_link(self, rschema, existant, card):
        return False

    # controller side method (eg POST reception handling)

    def actual_eid(self, eid):
        # should be either an int (existant entity) or a variable (to be
        # created entity)
        assert eid or eid == 0, repr(eid)  # 0 is a valid eid
        try:
            return int(eid)
        except ValueError:
            try:
                return self._cw.data['eidmap'][eid]
            except KeyError:
                self._cw.data['eidmap'][eid] = None
                return None

    def editable_relations(self):
        return ()
示例#23
0
class AutoClickAndEditFormView(EntityView):
    __regid__ = 'reledit'
    __select__ = non_final_entity() & match_kwargs('rtype')

    # ui side continuations
    _onclick = (
        u"cw.reledit.loadInlineEditionForm('%(formid)s', %(eid)s, '%(rtype)s', '%(role)s', "
        "'%(divid)s', %(reload)s, '%(vid)s', '%(action)s');")
    _cancelclick = "cw.reledit.cleanupAfterCancel('%s')"

    # ui side actions/buttons
    _addzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
    _addmsg = _('click to add a value')
    _addlogo = 'plus.png'
    _deletezone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
    _deletemsg = _('click to delete this value')
    _deletelogo = 'cancel.png'
    _editzone = u'<img title="%(msg)s" src="%(logo)s" alt="%(msg)s"/>'
    _editzonemsg = _('click to edit this field')
    _editlogo = 'pen_icon.png'

    # renderer
    _form_renderer_id = 'base'

    def entity_call(
            self,
            entity,
            rtype=None,
            role='subject',
            reload=False,  # controls reloading the whole page after change
            # boolean, eid (to redirect), or
            # function taking the subject entity & returning a boolean or an eid
        rvid=None,  # vid to be applied to other side of rtype (non final relations only)
            default_value=None,
            formid='base',
            action=None):
        """display field to edit entity's `rtype` relation on click"""
        assert rtype
        self._cw.add_css('cubicweb.form.css')
        self._cw.add_js(
            ('cubicweb.reledit.js', 'cubicweb.edition.js', 'cubicweb.ajax.js'))
        self.entity = entity
        rschema = self._cw.vreg.schema[rtype]
        rctrl = self._cw.vreg['uicfg'].select('reledit',
                                              self._cw,
                                              entity=entity)
        self._rules = rctrl.etype_get(self.entity.e_schema.type, rschema.type,
                                      role, '*')
        reload = self._compute_reload(rschema, role, reload)
        divid = self._build_divid(rtype, role, self.entity.eid)
        if rschema.final:
            self._handle_attribute(rschema, role, divid, reload, action)
        else:
            if self._is_composite():
                self._handle_composite(rschema, role, divid, reload, formid,
                                       action)
            else:
                self._handle_relation(rschema, role, divid, reload, formid,
                                      action)

    def _handle_attribute(self, rschema, role, divid, reload, action):
        rvid = self._rules.get('rvid', None)
        if rvid is not None:
            value = self._cw.view(rvid,
                                  entity=self.entity,
                                  rtype=rschema.type,
                                  role=role)
        else:
            value = self.entity.printable_value(rschema.type)
        if not self._should_edit_attribute(rschema):
            self.w(value)
            return
        form, renderer = self._build_form(self.entity, rschema, role, divid,
                                          'base', reload, action)
        value = value or self._compute_default_value(rschema, role)
        self.view_form(divid, value, form, renderer)

    def _compute_formid_value(self, rschema, role, rvid, formid):
        related_rset = self.entity.related(rschema.type, role)
        if related_rset:
            value = self._cw.view(rvid, related_rset)
        else:
            value = self._compute_default_value(rschema, role)
        if not self._should_edit_relation(rschema, role):
            return None, value
        return formid, value

    def _handle_relation(self, rschema, role, divid, reload, formid, action):
        rvid = self._rules.get('rvid', 'autolimited')
        formid, value = self._compute_formid_value(rschema, role, rvid, formid)
        if formid is None:
            return self.w(value)
        form, renderer = self._build_form(self.entity, rschema, role,
                                          divid, formid, reload, action,
                                          dict(vid=rvid))
        self.view_form(divid, value, form, renderer)

    def _handle_composite(self, rschema, role, divid, reload, formid, action):
        # this is for attribute-like composites (1 target type, 1 related entity at most, for now)
        entity = self.entity
        related_rset = entity.related(rschema.type, role)
        add_related = self._may_add_related(related_rset, rschema, role)
        edit_related = self._may_edit_related_entity(related_rset, rschema,
                                                     role)
        delete_related = edit_related and self._may_delete_related(
            related_rset, rschema, role)
        rvid = self._rules.get('rvid', 'autolimited')
        formid, value = self._compute_formid_value(rschema, role, rvid, formid)
        if formid is None or not (edit_related or add_related):
            # till we learn to handle cases where not (edit_related or add_related)
            self.w(value)
            return
        form, renderer = self._build_form(entity, rschema, role, divid, formid,
                                          reload, action, dict(vid=rvid))
        self.view_form(divid, value, form, renderer, edit_related, add_related,
                       delete_related)

    @cached
    def _compute_ttypes(self, rschema, role):
        dual_role = neg_role(role)
        return getattr(rschema, '%ss' % dual_role)()

    def _compute_reload(self, rschema, role, reload):
        ctrl_reload = self._rules.get('reload', reload)
        if callable(ctrl_reload):
            ctrl_reload = ctrl_reload(self.entity)
        if isinstance(ctrl_reload, int) and ctrl_reload > 1:  # not True/False
            ctrl_reload = self._cw.build_url(ctrl_reload)
        return ctrl_reload

    def _compute_default_value(self, rschema, role):
        default = self._rules.get('novalue_label')
        if default is None:
            if self._rules.get('novalue_include_rtype'):
                default = self._cw._('<%s not specified>') % display_name(
                    self._cw, rschema.type, role)
            else:
                default = self._cw._('<not specified>')
        else:
            default = self._cw._(default)
        return xml_escape(default)

    def _is_composite(self):
        return self._rules.get('edit_target') == 'related'

    def _may_add_related(self, related_rset, rschema, role):
        """ ok for attribute-like composite entities """
        ttypes = self._compute_ttypes(rschema, role)
        if len(ttypes) > 1:  # many etypes: learn how to do it
            return False
        rdef = rschema.role_rdef(self.entity.e_schema, ttypes[0], role)
        card = rdef.role_cardinality(role)
        if related_rset or card not in '?1':
            return False
        if role == 'subject':
            kwargs = {'fromeid': self.entity.eid}
        else:
            kwargs = {'toeid': self.entity.eid}
        return rdef.has_perm(self._cw, 'add', **kwargs)

    def _may_edit_related_entity(self, related_rset, rschema, role):
        """ controls the edition of the related entity """
        ttypes = self._compute_ttypes(rschema, role)
        if len(ttypes) > 1 or len(related_rset.rows) != 1:
            return False
        if self.entity.e_schema.rdef(rschema,
                                     role).role_cardinality(role) not in '?1':
            return False
        return related_rset.get_entity(0, 0).cw_has_perm('update')

    def _may_delete_related(self, related_rset, rschema, role):
        # we assume may_edit_related, only 1 related entity
        if not related_rset:
            return False
        rentity = related_rset.get_entity(0, 0)
        entity = self.entity
        if role == 'subject':
            kwargs = {'fromeid': entity.eid, 'toeid': rentity.eid}
            cardinality = rschema.rdefs[(entity.cw_etype,
                                         rentity.cw_etype)].cardinality[0]
        else:
            kwargs = {'fromeid': rentity.eid, 'toeid': entity.eid}
            cardinality = rschema.rdefs[(rentity.cw_etype,
                                         entity.cw_etype)].cardinality[1]
        if cardinality in '1+':
            return False
        # NOTE: should be sufficient given a well built schema/security
        return rschema.has_perm(self._cw, 'delete', **kwargs)

    def _build_zone(self, zonedef, msg, logo):
        return zonedef % {
            'msg': xml_escape(self._cw._(msg)),
            'logo': xml_escape(self._cw.data_url(logo))
        }

    def _build_edit_zone(self):
        return self._build_zone(self._editzone, self._editzonemsg,
                                self._editlogo)

    def _build_delete_zone(self):
        return self._build_zone(self._deletezone, self._deletemsg,
                                self._deletelogo)

    def _build_add_zone(self):
        return self._build_zone(self._addzone, self._addmsg, self._addlogo)

    def _build_divid(self, rtype, role, entity_eid):
        """ builds an id for the root div of a reledit widget """
        return '%s-%s-%s' % (rtype, role, entity_eid)

    def _build_args(self,
                    entity,
                    rtype,
                    role,
                    formid,
                    reload,
                    action,
                    extradata=None):
        divid = self._build_divid(rtype, role, entity.eid)
        event_args = {
            'divid': divid,
            'eid': entity.eid,
            'rtype': rtype,
            'formid': formid,
            'reload': json_dumps(reload),
            'action': action,
            'role': role,
            'vid': u''
        }
        if extradata:
            event_args.update(extradata)
        return event_args

    def _prepare_form(self, entity, rschema, role, action):
        assert action in ('edit_rtype', 'edit_related', 'add',
                          'delete'), action
        if action == 'edit_rtype':
            return False, entity
        label = True
        if action in ('edit_related', 'delete'):
            edit_entity = entity.related(rschema, role).get_entity(0, 0)
        elif action == 'add':
            add_etype = self._compute_ttypes(rschema, role)[0]
            _new_entity = self._cw.vreg['etypes'].etype_class(add_etype)(
                self._cw)
            _new_entity.eid = next(self._cw.varmaker)
            edit_entity = _new_entity
            # XXX see forms.py ~ 276 and entities.linked_to method
            #     is there another way?
            self._cw.form['__linkto'] = '%s:%s:%s' % (rschema, entity.eid,
                                                      neg_role(role))
        assert edit_entity
        return label, edit_entity

    def _build_renderer(self, related_entity, display_label):
        return self._cw.vreg['formrenderers'].select(
            self._form_renderer_id,
            self._cw,
            entity=related_entity,
            display_label=display_label,
            table_class='attributeForm' if display_label else '',
            display_help=False,
            button_bar_class='buttonbar',
            display_progress_div=False)

    def _build_form(self,
                    entity,
                    rschema,
                    role,
                    divid,
                    formid,
                    reload,
                    action,
                    extradata=None,
                    **formargs):
        rtype = rschema.type
        event_args = self._build_args(entity, rtype, role, formid, reload,
                                      action, extradata)
        if not action:
            form = _DummyForm()
            form.event_args = event_args
            return form, None
        label, edit_entity = self._prepare_form(entity, rschema, role, action)
        cancelclick = self._cancelclick % divid
        form = self._cw.vreg['forms'].select(
            formid,
            self._cw,
            rset=edit_entity.as_rset(),
            entity=edit_entity,
            domid='%s-form' % divid,
            formtype='inlined',
            action=self._cw.build_url(
                'validateform',
                __onsuccess='window.parent.cw.reledit.onSuccess'),
            cwtarget='eformframe',
            cssclass='releditForm',
            **formargs)
        # pass reledit arguments
        for pname, pvalue in event_args.items():
            form.add_hidden('__reledit|' + pname, pvalue)
        # handle buttons
        if form.form_buttons:  # edition, delete
            form_buttons = []
            for button in form.form_buttons:
                if not button.label.endswith('apply'):
                    if button.label.endswith('cancel'):
                        button = copy.deepcopy(button)
                        button.cwaction = None
                        button.onclick = cancelclick
                        if 'class' in button.attrs:
                            new_class = button.attrs['class'].replace(
                                'cwjs-edition-cancel', '')
                            button.attrs['class'] = new_class
                    form_buttons.append(button)
            form.form_buttons = form_buttons
        else:  # base
            form.form_buttons = [
                SubmitButton(),
                Button(stdmsgs.BUTTON_CANCEL, onclick=cancelclick)
            ]
        form.event_args = event_args
        if formid == 'base':
            field = form.field_by_name(rtype, role, entity.e_schema)
            form.append_field(field)
        return form, self._build_renderer(edit_entity, label)

    def _should_edit_attribute(self, rschema):
        entity = self.entity
        rdef = entity.e_schema.rdef(rschema)
        # check permissions
        if not entity.cw_has_perm('update'):
            return False
        rdef = entity.e_schema.rdef(rschema)
        return rdef.has_perm(self._cw, 'update', eid=entity.eid)

    def _should_edit_relation(self, rschema, role):
        eeid = self.entity.eid
        perm_args = {'fromeid': eeid} if role == 'subject' else {'toeid': eeid}
        return rschema.has_perm(self._cw, 'add', **perm_args)

    def _open_form_wrapper(self, divid, value, form, renderer, _edit_related,
                           _add_related, _delete_related):
        w = self.w
        w(
            u'<div id="%(id)s-reledit" onmouseout="%(out)s" onmouseover="%(over)s" class="%(css)s">'
            % {
                'id': divid,
                'css': 'releditField',
                'out': "jQuery('#%s').addClass('invisible')" % divid,
                'over': "jQuery('#%s').removeClass('invisible')" % divid
            })
        w(u'<div id="%s-value" class="editableFieldValue">' % divid)
        w(value)
        w(u'</div>')
        form.render(w=w, renderer=renderer)
        w(u'<div id="%s" class="editableField invisible">' % divid)

    def _edit_action(self, divid, args, edit_related, add_related,
                     _delete_related):
        # XXX disambiguate wrt edit_related
        if not add_related:  # currently, excludes edition
            w = self.w
            args['formid'] = 'edition' if edit_related else 'base'
            args['action'] = 'edit_related' if edit_related else 'edit_rtype'
            w(u'<div id="%s-update" class="editableField" onclick="%s" title="%s">'
              % (divid, xml_escape(
                  self._onclick % args), self._cw._(self._editzonemsg)))
            w(self._build_edit_zone())
            w(u'</div>')

    def _add_action(self, divid, args, _edit_related, add_related,
                    _delete_related):
        if add_related:
            w = self.w
            args['formid'] = 'edition'
            args['action'] = 'add'
            w(u'<div id="%s-add" class="editableField" onclick="%s" title="%s">'
              % (divid, xml_escape(
                  self._onclick % args), self._cw._(self._addmsg)))
            w(self._build_add_zone())
            w(u'</div>')

    def _del_action(self, divid, args, _edit_related, _add_related,
                    delete_related):
        if delete_related:
            w = self.w
            args['formid'] = 'deleteconf'
            args['action'] = 'delete'
            w(u'<div id="%s-delete" class="editableField" onclick="%s" title="%s">'
              % (divid, xml_escape(
                  self._onclick % args), self._cw._(self._deletemsg)))
            w(self._build_delete_zone())
            w(u'</div>')

    def _close_form_wrapper(self):
        self.w(u'</div>')
        self.w(u'</div>')

    def view_form(self,
                  divid,
                  value,
                  form=None,
                  renderer=None,
                  edit_related=False,
                  add_related=False,
                  delete_related=False):
        self._open_form_wrapper(divid, value, form, renderer, edit_related,
                                add_related, delete_related)
        args = form.event_args.copy()
        self._edit_action(divid, args, edit_related, add_related,
                          delete_related)
        self._add_action(divid, args, edit_related, add_related,
                         delete_related)
        self._del_action(divid, args, edit_related, add_related,
                         delete_related)
        self._close_form_wrapper()