web/views/editforms.py
changeset 4387 4aacd6492ef4
parent 4380 5613d7c06339
child 4641 9d8903b04031
equal deleted inserted replaced
4386:cf8842b69379 4387:4aacd6492ef4
    14 from simplejson import dumps
    14 from simplejson import dumps
    15 
    15 
    16 from logilab.mtconverter import xml_escape
    16 from logilab.mtconverter import xml_escape
    17 from logilab.common.decorators import cached
    17 from logilab.common.decorators import cached
    18 
    18 
    19 from cubicweb import neg_role
       
    20 from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
    19 from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
    21                                 specified_etype_implements, yes)
    20                                 specified_etype_implements, yes)
    22 from cubicweb.view import EntityView
    21 from cubicweb.view import EntityView
    23 from cubicweb import tags
    22 from cubicweb import tags
    24 from cubicweb.web import uicfg, stdmsgs, eid_param, \
    23 from cubicweb.web import uicfg, stdmsgs, eid_param, \
    79             # edition form
    78             # edition form
    80             w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
    79             w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
    81                                       href=entity.absolute_url()))
    80                                       href=entity.absolute_url()))
    82         w(u'</ul>\n')
    81         w(u'</ul>\n')
    83         w(form.render())
    82         w(form.render())
       
    83 
       
    84 
       
    85 class EditionFormView(FormViewMixIn, EntityView):
       
    86     """display primary entity edition form"""
       
    87     __regid__ = 'edition'
       
    88     # add yes() so it takes precedence over deprecated views in baseforms,
       
    89     # though not baseforms based customized view
       
    90     __select__ = one_line_rset() & non_final_entity() & yes()
       
    91 
       
    92     title = _('edition')
       
    93 
       
    94     def cell_call(self, row, col, **kwargs):
       
    95         entity = self.cw_rset.complete_entity(row, col)
       
    96         self.render_form(entity)
       
    97 
       
    98     def render_form(self, entity):
       
    99         """fetch and render the form"""
       
   100         self.form_title(entity)
       
   101         form = self._cw.vreg['forms'].select('edition', self._cw, rset=entity.cw_rset,
       
   102                                              row=entity.cw_row, col=entity.cw_col,
       
   103                                              entity=entity,
       
   104                                              submitmsg=self.submited_message())
       
   105         self.init_form(form, entity)
       
   106         self.w(form.render(formvid=u'edition'))
       
   107 
       
   108     def init_form(self, form, entity):
       
   109         """customize your form before rendering here"""
       
   110         pass
       
   111 
       
   112     def form_title(self, entity):
       
   113         """the form view title"""
       
   114         ptitle = self._cw._(self.title)
       
   115         self.w(u'<div class="formTitle"><span>%s %s</span></div>' % (
       
   116             entity.dc_type(), ptitle and '(%s)' % ptitle))
       
   117 
       
   118     def submited_message(self):
       
   119         """return the message that will be displayed on successful edition"""
       
   120         return self._cw._('entity edited')
       
   121 
       
   122 
       
   123 class CreationFormView(EditionFormView):
       
   124     """display primary entity creation form"""
       
   125     __regid__ = 'creation'
       
   126     __select__ = specified_etype_implements('Any') & yes()
       
   127 
       
   128     title = _('creation')
       
   129 
       
   130     def call(self, **kwargs):
       
   131         """creation view for an entity"""
       
   132         # at this point we know etype is a valid entity type, thanks to our
       
   133         # selector
       
   134         etype = kwargs.pop('etype', self._cw.form.get('etype'))
       
   135         entity = self._cw.vreg['etypes'].etype_class(etype)(self._cw)
       
   136         entity.eid = self._cw.varmaker.next()
       
   137         self.render_form(entity)
       
   138 
       
   139     def form_title(self, entity):
       
   140         """the form view title"""
       
   141         if '__linkto' in self._cw.form:
       
   142             if isinstance(self._cw.form['__linkto'], list):
       
   143                 # XXX which one should be considered (case: add a ticket to a
       
   144                 # version in jpl)
       
   145                 rtype, linkto_eid, role = self._cw.form['__linkto'][0].split(':')
       
   146             else:
       
   147                 rtype, linkto_eid, role = self._cw.form['__linkto'].split(':')
       
   148             linkto_rset = self._cw.eid_rset(linkto_eid)
       
   149             linkto_type = linkto_rset.description[0][0]
       
   150             if role == 'subject':
       
   151                 title = self._cw.__('creating %s (%s %s %s %%(linkto)s)' % (
       
   152                     entity.e_schema, entity.e_schema, rtype, linkto_type))
       
   153             else:
       
   154                 title = self._cw.__('creating %s (%s %%(linkto)s %s %s)' % (
       
   155                     entity.e_schema, linkto_type, rtype, entity.e_schema))
       
   156             msg = title % {'linkto' : self._cw.view('incontext', linkto_rset)}
       
   157             self.w(u'<div class="formTitle notransform"><span>%s</span></div>' % msg)
       
   158         else:
       
   159             super(CreationFormView, self).form_title(entity)
       
   160 
       
   161     def url(self):
       
   162         """return the url associated with this view"""
       
   163         return self.create_url(self._cw.form.get('etype'))
       
   164 
       
   165     def submited_message(self):
       
   166         """return the message that will be displayed on successful edition"""
       
   167         return self._cw._('entity created')
       
   168 
       
   169 
       
   170 class CopyFormView(EditionFormView):
       
   171     """display primary entity creation form initialized with values from another
       
   172     entity
       
   173     """
       
   174     __regid__ = 'copy'
       
   175 
       
   176     title = _('copy')
       
   177     warning_message = _('Please note that this is only a shallow copy')
       
   178 
       
   179     def render_form(self, entity):
       
   180         """fetch and render the form"""
       
   181         # make a copy of entity to avoid altering the entity in the
       
   182         # request's cache.
       
   183         entity.complete()
       
   184         self.newentity = copy(entity)
       
   185         self.copying = entity
       
   186         self.newentity.eid = self._cw.varmaker.next()
       
   187         self.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'
       
   188                % self._cw._(self.warning_message))
       
   189         super(CopyFormView, self).render_form(self.newentity)
       
   190         del self.newentity
       
   191 
       
   192     def init_form(self, form, entity):
       
   193         """customize your form before rendering here"""
       
   194         super(CopyFormView, self).init_form(form, entity)
       
   195         if entity.eid == self.newentity.eid:
       
   196             form.add_hidden(eid_param('__cloned_eid', entity.eid),
       
   197                             self.copying.eid)
       
   198         for rschema, role in form.editable_attributes():
       
   199             if not rschema.final:
       
   200                 # ensure relation cache is filed
       
   201                 rset = self.copying.related(rschema, role)
       
   202                 self.newentity.set_related_cache(rschema, role, rset)
       
   203 
       
   204     def submited_message(self):
       
   205         """return the message that will be displayed on successful edition"""
       
   206         return self._cw._('entity copied')
       
   207 
       
   208 
       
   209 class TableEditForm(forms.CompositeForm):
       
   210     __regid__ = 'muledit'
       
   211     domid = 'entityForm'
       
   212     onsubmit = "return validateForm('%s', null);" % domid
       
   213     form_buttons = [fw.SubmitButton(_('validate modifications on selected items')),
       
   214                     fw.ResetButton(_('revert changes'))]
       
   215 
       
   216     def __init__(self, req, rset, **kwargs):
       
   217         kwargs.setdefault('__redirectrql', rset.printable_rql())
       
   218         super(TableEditForm, self).__init__(req, rset=rset, **kwargs)
       
   219         for row in xrange(len(self.cw_rset)):
       
   220             form = self._cw.vreg['forms'].select('edition', self._cw,
       
   221                                                  rset=self.cw_rset, row=row,
       
   222                                                  formtype='muledit',
       
   223                                                  copy_nav_params=False,
       
   224                                                  mainform=False)
       
   225             # XXX rely on the EntityCompositeFormRenderer to put the eid input
       
   226             form.remove_field(form.field_by_name('eid'))
       
   227             self.add_subform(form)
       
   228 
       
   229 
       
   230 class TableEditFormView(FormViewMixIn, EntityView):
       
   231     __regid__ = 'muledit'
       
   232     __select__ = EntityView.__select__ & yes()
       
   233     title = _('multiple edit')
       
   234 
       
   235     def call(self, **kwargs):
       
   236         """a view to edit multiple entities of the same type the first column
       
   237         should be the eid
       
   238         """
       
   239         #self.form_title(entity)
       
   240         form = self._cw.vreg['forms'].select(self.__regid__, self._cw,
       
   241                                              rset=self.cw_rset,
       
   242                                              copy_nav_params=True)
       
   243         # XXX overriding formvid (eg __form_id) necessary to make work edition:
       
   244         # the edit controller try to select the form with no rset but
       
   245         # entity=entity, and use this form to edit the entity. So we want
       
   246         # edition form there but specifying formvid may have other undesired
       
   247         # side effect. Maybe we should provide another variable optinally
       
   248         # telling which form the edit controller should select (eg difffers
       
   249         # between html generation / post handling form)
       
   250         self.w(form.render(formvid='edition'))
       
   251 
       
   252 
       
   253 # click and edit handling ('reledit') ##########################################
       
   254 
       
   255 class DummyForm(object):
       
   256     __slots__ = ('event_args',)
       
   257     def form_render(self, **_args):
       
   258         return u''
       
   259     def render(self, **_args):
       
   260         return u''
       
   261     def append_field(self, *args):
       
   262         pass
       
   263     def field_by_name(self, rtype, role, eschema=None):
       
   264         return None
    84 
   265 
    85 
   266 
    86 class ClickAndEditFormView(FormViewMixIn, EntityView):
   267 class ClickAndEditFormView(FormViewMixIn, EntityView):
    87     """form used to permit ajax edition of a relation or attribute of an entity
   268     """form used to permit ajax edition of a relation or attribute of an entity
    88     in a view, if logged user have the permission to edit it.
   269     in a view, if logged user have the permission to edit it.
   234             **formargs)
   415             **formargs)
   235         form.event_args = event_args
   416         form.event_args = event_args
   236         return form
   417         return form
   237 
   418 
   238 
   419 
   239 class DummyForm(object):
       
   240     __slots__ = ('event_args',)
       
   241     def form_render(self, **_args):
       
   242         return u''
       
   243     def render(self, **_args):
       
   244         return u''
       
   245     def append_field(self, *args):
       
   246         pass
       
   247     def field_by_name(self, rtype, role, eschema=None):
       
   248         return None
       
   249 
       
   250 
       
   251 class AutoClickAndEditFormView(ClickAndEditFormView):
   420 class AutoClickAndEditFormView(ClickAndEditFormView):
   252     """same as ClickAndEditFormView but checking if the view *should* be applied
   421     """same as ClickAndEditFormView but checking if the view *should* be applied
   253     by checking uicfg configuration and composite relation property.
   422     by checking uicfg configuration and composite relation property.
   254     """
   423     """
   255     __regid__ = 'reledit'
   424     __regid__ = 'reledit'
   278         return form
   447         return form
   279 
   448 
   280     def _build_renderer(self, entity, rtype, role):
   449     def _build_renderer(self, entity, rtype, role):
   281         pass
   450         pass
   282 
   451 
   283 
       
   284 class EditionFormView(FormViewMixIn, EntityView):
       
   285     """display primary entity edition form"""
       
   286     __regid__ = 'edition'
       
   287     # add yes() so it takes precedence over deprecated views in baseforms,
       
   288     # though not baseforms based customized view
       
   289     __select__ = one_line_rset() & non_final_entity() & yes()
       
   290 
       
   291     title = _('edition')
       
   292 
       
   293     def cell_call(self, row, col, **kwargs):
       
   294         entity = self.cw_rset.complete_entity(row, col)
       
   295         self.render_form(entity)
       
   296 
       
   297     def render_form(self, entity):
       
   298         """fetch and render the form"""
       
   299         self.form_title(entity)
       
   300         form = self._cw.vreg['forms'].select('edition', self._cw, rset=entity.cw_rset,
       
   301                                              row=entity.cw_row, col=entity.cw_col,
       
   302                                              entity=entity,
       
   303                                              submitmsg=self.submited_message())
       
   304         self.init_form(form, entity)
       
   305         self.w(form.render(formvid=u'edition'))
       
   306 
       
   307     def init_form(self, form, entity):
       
   308         """customize your form before rendering here"""
       
   309         pass
       
   310 
       
   311     def form_title(self, entity):
       
   312         """the form view title"""
       
   313         ptitle = self._cw._(self.title)
       
   314         self.w(u'<div class="formTitle"><span>%s %s</span></div>' % (
       
   315             entity.dc_type(), ptitle and '(%s)' % ptitle))
       
   316 
       
   317     def submited_message(self):
       
   318         """return the message that will be displayed on successful edition"""
       
   319         return self._cw._('entity edited')
       
   320 
       
   321 
       
   322 class CreationFormView(EditionFormView):
       
   323     """display primary entity creation form"""
       
   324     __regid__ = 'creation'
       
   325     __select__ = specified_etype_implements('Any') & yes()
       
   326 
       
   327     title = _('creation')
       
   328 
       
   329     def call(self, **kwargs):
       
   330         """creation view for an entity"""
       
   331         # at this point we know etype is a valid entity type, thanks to our
       
   332         # selector
       
   333         etype = kwargs.pop('etype', self._cw.form.get('etype'))
       
   334         entity = self._cw.vreg['etypes'].etype_class(etype)(self._cw)
       
   335         entity.eid = self._cw.varmaker.next()
       
   336         self.render_form(entity)
       
   337 
       
   338     def form_title(self, entity):
       
   339         """the form view title"""
       
   340         if '__linkto' in self._cw.form:
       
   341             if isinstance(self._cw.form['__linkto'], list):
       
   342                 # XXX which one should be considered (case: add a ticket to a
       
   343                 # version in jpl)
       
   344                 rtype, linkto_eid, role = self._cw.form['__linkto'][0].split(':')
       
   345             else:
       
   346                 rtype, linkto_eid, role = self._cw.form['__linkto'].split(':')
       
   347             linkto_rset = self._cw.eid_rset(linkto_eid)
       
   348             linkto_type = linkto_rset.description[0][0]
       
   349             if role == 'subject':
       
   350                 title = self._cw.__('creating %s (%s %s %s %%(linkto)s)' % (
       
   351                     entity.e_schema, entity.e_schema, rtype, linkto_type))
       
   352             else:
       
   353                 title = self._cw.__('creating %s (%s %%(linkto)s %s %s)' % (
       
   354                     entity.e_schema, linkto_type, rtype, entity.e_schema))
       
   355             msg = title % {'linkto' : self._cw.view('incontext', linkto_rset)}
       
   356             self.w(u'<div class="formTitle notransform"><span>%s</span></div>' % msg)
       
   357         else:
       
   358             super(CreationFormView, self).form_title(entity)
       
   359 
       
   360     def url(self):
       
   361         """return the url associated with this view"""
       
   362         return self.create_url(self._cw.form.get('etype'))
       
   363 
       
   364     def submited_message(self):
       
   365         """return the message that will be displayed on successful edition"""
       
   366         return self._cw._('entity created')
       
   367 
       
   368 
       
   369 class CopyFormView(EditionFormView):
       
   370     """display primary entity creation form initialized with values from another
       
   371     entity
       
   372     """
       
   373     __regid__ = 'copy'
       
   374 
       
   375     title = _('copy')
       
   376     warning_message = _('Please note that this is only a shallow copy')
       
   377 
       
   378     def render_form(self, entity):
       
   379         """fetch and render the form"""
       
   380         # make a copy of entity to avoid altering the entity in the
       
   381         # request's cache.
       
   382         entity.complete()
       
   383         self.newentity = copy(entity)
       
   384         self.copying = entity
       
   385         self.newentity.eid = self._cw.varmaker.next()
       
   386         self.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'
       
   387                % self._cw._(self.warning_message))
       
   388         super(CopyFormView, self).render_form(self.newentity)
       
   389         del self.newentity
       
   390 
       
   391     def init_form(self, form, entity):
       
   392         """customize your form before rendering here"""
       
   393         super(CopyFormView, self).init_form(form, entity)
       
   394         if entity.eid == self.newentity.eid:
       
   395             form.add_hidden(eid_param('__cloned_eid', entity.eid),
       
   396                             self.copying.eid)
       
   397         for rschema, role in form.editable_attributes():
       
   398             if not rschema.final:
       
   399                 # ensure relation cache is filed
       
   400                 rset = self.copying.related(rschema, role)
       
   401                 self.newentity.set_related_cache(rschema, role, rset)
       
   402 
       
   403     def submited_message(self):
       
   404         """return the message that will be displayed on successful edition"""
       
   405         return self._cw._('entity copied')
       
   406 
       
   407 
       
   408 class TableEditForm(forms.CompositeForm):
       
   409     __regid__ = 'muledit'
       
   410     domid = 'entityForm'
       
   411     onsubmit = "return validateForm('%s', null);" % domid
       
   412     form_buttons = [fw.SubmitButton(_('validate modifications on selected items')),
       
   413                     fw.ResetButton(_('revert changes'))]
       
   414 
       
   415     def __init__(self, req, rset, **kwargs):
       
   416         kwargs.setdefault('__redirectrql', rset.printable_rql())
       
   417         super(TableEditForm, self).__init__(req, rset=rset, **kwargs)
       
   418         for row in xrange(len(self.cw_rset)):
       
   419             form = self._cw.vreg['forms'].select('edition', self._cw,
       
   420                                                  rset=self.cw_rset, row=row,
       
   421                                                  formtype='muledit',
       
   422                                                  copy_nav_params=False,
       
   423                                                  mainform=False)
       
   424             # XXX rely on the EntityCompositeFormRenderer to put the eid input
       
   425             form.remove_field(form.field_by_name('eid'))
       
   426             self.add_subform(form)
       
   427 
       
   428 
       
   429 class TableEditFormView(FormViewMixIn, EntityView):
       
   430     __regid__ = 'muledit'
       
   431     __select__ = EntityView.__select__ & yes()
       
   432     title = _('multiple edit')
       
   433 
       
   434     def call(self, **kwargs):
       
   435         """a view to edit multiple entities of the same type the first column
       
   436         should be the eid
       
   437         """
       
   438         #self.form_title(entity)
       
   439         form = self._cw.vreg['forms'].select(self.__regid__, self._cw,
       
   440                                              rset=self.cw_rset,
       
   441                                              copy_nav_params=True)
       
   442         # XXX overriding formvid (eg __form_id) necessary to make work edition:
       
   443         # the edit controller try to select the form with no rset but
       
   444         # entity=entity, and use this form to edit the entity. So we want
       
   445         # edition form there but specifying formvid may have other undesired
       
   446         # side effect. Maybe we should provide another variable optinally
       
   447         # telling which form the edit controller should select (eg difffers
       
   448         # between html generation / post handling form)
       
   449         self.w(form.render(formvid='edition'))
       
   450 
       
   451 
       
   452 # inlined form handling ########################################################
       
   453 
       
   454 class InlinedFormField(ff.Field):
       
   455     def __init__(self, view=None, **kwargs):
       
   456         if view.role == 'object':
       
   457             fieldset = u'%s_object%s' % view.rtype
       
   458         else:
       
   459             fieldset = view.rtype
       
   460         #kwargs.setdefault('fieldset', fieldset)
       
   461         kwargs.setdefault('label', None)
       
   462         super(InlinedFormField, self).__init__(name=view.rtype, role=view.role,
       
   463                                                eidparam=True, **kwargs)
       
   464         self.view = view
       
   465 
       
   466     def render(self, form, renderer):
       
   467         """render this field, which is part of form, using the given form
       
   468         renderer
       
   469         """
       
   470         view = self.view
       
   471         i18nctx = 'inlined:%s.%s.%s' % (form.edited_entity.e_schema,
       
   472                                         view.rtype, view.role)
       
   473         return u'<div class="inline-%s-%s-slot">%s</div>' % (
       
   474             view.rtype, view.role,
       
   475             view.render(i18nctx=i18nctx, row=view.cw_row, col=view.cw_col))
       
   476 
       
   477     def form_init(self, form):
       
   478         """method called before by build_context to trigger potential field
       
   479         initialization requiring the form instance
       
   480         """
       
   481         if self.view.form:
       
   482             self.view.form.build_context(form.formvalues)
       
   483 
       
   484     @property
       
   485     def needs_multipart(self):
       
   486         if self.view.form:
       
   487             # take a look at inlined forms to check (recursively) if they need
       
   488             # multipart handling.
       
   489             return self.view.form.needs_multipart
       
   490         return False
       
   491 
       
   492     def has_been_modified(self, form):
       
   493         return False
       
   494 
       
   495     def process_posted(self, form):
       
   496         pass # handled by the subform
       
   497 
       
   498 
       
   499 class InlineEntityEditionFormView(FormViewMixIn, EntityView):
       
   500     """
       
   501     :attr peid: the parent entity's eid hosting the inline form
       
   502     :attr rtype: the relation bridging `etype` and `peid`
       
   503     :attr role: the role played by the `peid` in the relation
       
   504     :attr pform: the parent form where this inlined form is being displayed
       
   505     """
       
   506     __regid__ = 'inline-edition'
       
   507     __select__ = non_final_entity() & match_kwargs('peid', 'rtype')
       
   508 
       
   509     _select_attrs = ('peid', 'rtype', 'role', 'pform', 'etype')
       
   510     removejs = "removeInlinedEntity('%s', '%s', '%s')"
       
   511 
       
   512     def __init__(self, *args, **kwargs):
       
   513         for attr in self._select_attrs:
       
   514             setattr(self, attr, kwargs.pop(attr, None))
       
   515         super(InlineEntityEditionFormView, self).__init__(*args, **kwargs)
       
   516 
       
   517     def _entity(self):
       
   518         assert self.cw_row is not None, self
       
   519         return self.cw_rset.get_entity(self.cw_row, self.cw_col)
       
   520 
       
   521     @property
       
   522     @cached
       
   523     def form(self):
       
   524         entity = self._entity()
       
   525         form = self._cw.vreg['forms'].select('edition', self._cw,
       
   526                                              entity=entity,
       
   527                                              formtype='inlined',
       
   528                                              form_renderer_id='inline',
       
   529                                              copy_nav_params=False,
       
   530                                              mainform=False,
       
   531                                              parent_form=self.pform,
       
   532                                              **self.cw_extra_kwargs)
       
   533         if self.pform is None:
       
   534             form.restore_previous_post(form.session_key())
       
   535         #assert form.parent_form
       
   536         self.add_hiddens(form, entity)
       
   537         return form
       
   538 
       
   539     def cell_call(self, row, col, i18nctx, **kwargs):
       
   540         """
       
   541         :param peid: the parent entity's eid hosting the inline form
       
   542         :param rtype: the relation bridging `etype` and `peid`
       
   543         :param role: the role played by the `peid` in the relation
       
   544         """
       
   545         entity = self._entity()
       
   546         divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (
       
   547             self.peid, self.rtype, entity.eid)
       
   548         self.render_form(i18nctx, divonclick=divonclick, **kwargs)
       
   549 
       
   550     def render_form(self, i18nctx, **kwargs):
       
   551         """fetch and render the form"""
       
   552         entity = self._entity()
       
   553         divid = '%s-%s-%s' % (self.peid, self.rtype, entity.eid)
       
   554         title = self.form_title(entity, i18nctx)
       
   555         removejs = self.removejs and self.removejs % (
       
   556             self.peid, self.rtype, entity.eid)
       
   557         countkey = '%s_count' % self.rtype
       
   558         try:
       
   559             self._cw.data[countkey] += 1
       
   560         except KeyError:
       
   561             self._cw.data[countkey] = 1
       
   562         self.w(self.form.render(
       
   563             divid=divid, title=title, removejs=removejs, i18nctx=i18nctx,
       
   564             counter=self._cw.data[countkey] , **kwargs))
       
   565 
       
   566     def form_title(self, entity, i18nctx):
       
   567         return self._cw.pgettext(i18nctx, entity.__regid__)
       
   568 
       
   569     def add_hiddens(self, form, entity):
       
   570         """to ease overriding (see cubes.vcsfile.views.forms for instance)"""
       
   571         iid = 'rel-%s-%s-%s' % (self.peid, self.rtype, entity.eid)
       
   572         #  * str(self.rtype) in case it's a schema object
       
   573         #  * neged_role() since role is the for parent entity, we want the role
       
   574         #    of the inlined entity
       
   575         form.add_hidden(name=str(self.rtype), value=self.peid,
       
   576                         role=neg_role(self.role), eidparam=True, id=iid)
       
   577 
       
   578     def keep_entity(self, form, entity):
       
   579         if not entity.has_eid():
       
   580             return True
       
   581         # are we regenerating form because of a validation error ?
       
   582         if form.form_previous_values:
       
   583             cdvalues = self._cw.list_form_param(eid_param(self.rtype, self.peid),
       
   584                                                 form.form_previous_values)
       
   585             if unicode(entity.eid) not in cdvalues:
       
   586                 return False
       
   587         return True
       
   588 
       
   589 
       
   590 class InlineEntityCreationFormView(InlineEntityEditionFormView):
       
   591     """
       
   592     :attr etype: the entity type being created in the inline form
       
   593     """
       
   594     __regid__ = 'inline-creation'
       
   595     __select__ = (match_kwargs('peid', 'rtype')
       
   596                   & specified_etype_implements('Any'))
       
   597 
       
   598     @property
       
   599     def removejs(self):
       
   600         entity = self._entity()
       
   601         card = entity.e_schema.rdef(self.rtype, neg_role(self.role)).role_cardinality(self.role)
       
   602         # when one is adding an inline entity for a relation of a single card,
       
   603         # the 'add a new xxx' link disappears. If the user then cancel the addition,
       
   604         # we have to make this link appears back. This is done by giving add new link
       
   605         # id to removeInlineForm.
       
   606         if card not in '?1':
       
   607             return "removeInlineForm('%%s', '%%s', '%s', '%%s')" % self.role
       
   608         divid = "addNew%s%s%s:%s" % (
       
   609             self.etype, self.rtype, self.role, self.peid)
       
   610         return "removeInlineForm('%%s', '%%s', '%s', '%%s', '%s')" % (
       
   611             self.role, divid)
       
   612 
       
   613     @cached
       
   614     def _entity(self):
       
   615         try:
       
   616             cls = self._cw.vreg['etypes'].etype_class(self.etype)
       
   617         except:
       
   618             self.w(self._cw._('no such entity type %s') % etype)
       
   619             return
       
   620         entity = cls(self._cw)
       
   621         entity.eid = self._cw.varmaker.next()
       
   622         return entity
       
   623 
       
   624     def call(self, i18nctx, **kwargs):
       
   625         self.render_form(i18nctx, **kwargs)
       
   626 
       
   627 
       
   628 class InlineAddNewLinkView(InlineEntityCreationFormView):
       
   629     """
       
   630     :attr card: the cardinality of the relation according to role of `peid`
       
   631     """
       
   632     __regid__ = 'inline-addnew-link'
       
   633     __select__ = (match_kwargs('peid', 'rtype')
       
   634                   & specified_etype_implements('Any'))
       
   635 
       
   636     _select_attrs = InlineEntityCreationFormView._select_attrs + ('card',)
       
   637     form = None # no actual form wrapped
       
   638 
       
   639     def call(self, i18nctx, **kwargs):
       
   640         self._cw.set_varmaker()
       
   641         divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role, self.peid)
       
   642         self.w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
       
   643           % divid)
       
   644         js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % (
       
   645             self.peid, self.etype, self.rtype, self.role, i18nctx)
       
   646         if self.pform.should_hide_add_new_relation_link(self.rtype, self.card):
       
   647             js = "toggleVisibility('%s'); %s" % (divid, js)
       
   648         __ = self._cw.pgettext
       
   649         self.w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
       
   650           % (self.rtype, self.peid, js, __(i18nctx, 'add a %s' % self.etype)))
       
   651         self.w(u'</div>')