web/views/editforms.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """Set of HTML automatic forms to create, delete, copy or edit a single entity
       
    19 or a list of entities of the same type
       
    20 """
       
    21 
       
    22 __docformat__ = "restructuredtext en"
       
    23 from cubicweb import _
       
    24 
       
    25 from copy import copy
       
    26 
       
    27 from six.moves import range
       
    28 
       
    29 from logilab.mtconverter import xml_escape
       
    30 from logilab.common.decorators import cached
       
    31 from logilab.common.registry import yes
       
    32 from logilab.common.deprecation import class_moved
       
    33 
       
    34 from cubicweb import tags
       
    35 from cubicweb.predicates import (match_kwargs, one_line_rset, non_final_entity,
       
    36                                 specified_etype_implements, is_instance)
       
    37 from cubicweb.view import EntityView
       
    38 from cubicweb.schema import display_name
       
    39 from cubicweb.web import stdmsgs, eid_param, \
       
    40      formfields as ff, formwidgets as fw
       
    41 from cubicweb.web.form import FormViewMixIn, FieldNotFound
       
    42 from cubicweb.web.views import uicfg, forms, reledit
       
    43 
       
    44 _pvdc = uicfg.primaryview_display_ctrl
       
    45 
       
    46 
       
    47 class DeleteConfForm(forms.CompositeForm):
       
    48     __regid__ = 'deleteconf'
       
    49     # XXX non_final_entity does not implement eclass_selector
       
    50     __select__ = is_instance('Any')
       
    51 
       
    52     domid = 'deleteconf'
       
    53     copy_nav_params = True
       
    54     form_buttons = [fw.Button(stdmsgs.BUTTON_DELETE, cwaction='delete'),
       
    55                     fw.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
       
    56 
       
    57     def __init__(self, *args, **kwargs):
       
    58         super(DeleteConfForm, self).__init__(*args, **kwargs)
       
    59         done = set()
       
    60         for entity in self.cw_rset.entities():
       
    61             if entity.eid in done:
       
    62                 continue
       
    63             done.add(entity.eid)
       
    64             subform = self._cw.vreg['forms'].select('base', self._cw,
       
    65                                                     entity=entity,
       
    66                                                     mainform=False)
       
    67             self.add_subform(subform)
       
    68 
       
    69 
       
    70 class DeleteConfFormView(FormViewMixIn, EntityView):
       
    71     """form used to confirm deletion of some entities"""
       
    72     __regid__ = 'deleteconf'
       
    73     title = _('delete')
       
    74     # don't use navigation, all entities asked to be deleted should be displayed
       
    75     # else we will only delete the displayed page
       
    76     paginable = False
       
    77 
       
    78     def call(self, onsubmit=None):
       
    79         """ask for confirmation before real deletion"""
       
    80         req, w = self._cw, self.w
       
    81         _ = req._
       
    82         w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'
       
    83           % _('this action is not reversible!'))
       
    84         # XXX above message should have style of a warning
       
    85         w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s)?'))
       
    86         form = self._cw.vreg['forms'].select(self.__regid__, req,
       
    87                                              rset=self.cw_rset,
       
    88                                              onsubmit=onsubmit)
       
    89         w(u'<ul>\n')
       
    90         for entity in self.cw_rset.entities():
       
    91             # don't use outofcontext view or any other that may contain inline
       
    92             # edition form
       
    93             w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
       
    94                                       href=entity.absolute_url()))
       
    95         w(u'</ul>\n')
       
    96         form.render(w=self.w)
       
    97 
       
    98 
       
    99 class EditionFormView(FormViewMixIn, EntityView):
       
   100     """display primary entity edition form"""
       
   101     __regid__ = 'edition'
       
   102     # add yes() so it takes precedence over deprecated views in baseforms,
       
   103     # though not baseforms based customized view
       
   104     __select__ = one_line_rset() & non_final_entity() & yes()
       
   105     form_id = 'edition'
       
   106 
       
   107     title = _('modification')
       
   108 
       
   109     def cell_call(self, row, col, **kwargs):
       
   110         entity = self.cw_rset.complete_entity(row, col)
       
   111         self.render_form(entity)
       
   112 
       
   113     def render_form(self, entity):
       
   114         """fetch and render the form"""
       
   115         self.form_title(entity)
       
   116         form = self._cw.vreg['forms'].select(self.form_id, self._cw,
       
   117                                              entity=entity,
       
   118                                              submitmsg=self.submited_message())
       
   119         self.init_form(form, entity)
       
   120         form.render(w=self.w)
       
   121 
       
   122     def init_form(self, form, entity):
       
   123         """customize your form before rendering here"""
       
   124         pass
       
   125 
       
   126     def form_title(self, entity):
       
   127         """the form view title"""
       
   128         ptitle = self._cw._(self.title)
       
   129         self.w(u'<div class="formTitle"><span>%s %s</span></div>' % (
       
   130             entity.dc_type(), ptitle and '(%s)' % ptitle))
       
   131 
       
   132     def submited_message(self):
       
   133         """return the message that will be displayed on successful edition"""
       
   134         return self._cw._('entity edited')
       
   135 
       
   136 
       
   137 class CreationFormView(EditionFormView):
       
   138     """display primary entity creation form"""
       
   139     __regid__ = 'creation'
       
   140     __select__ = specified_etype_implements('Any') & yes()
       
   141 
       
   142     title = _('creation')
       
   143 
       
   144     def call(self, **kwargs):
       
   145         """creation view for an entity"""
       
   146         # at this point we know etype is a valid entity type, thanks to our
       
   147         # selector
       
   148         etype = kwargs.pop('etype', self._cw.form.get('etype'))
       
   149         entity = self._cw.vreg['etypes'].etype_class(etype)(self._cw)
       
   150         entity.eid = next(self._cw.varmaker)
       
   151         self.render_form(entity)
       
   152 
       
   153     def form_title(self, entity):
       
   154         """the form view title"""
       
   155         if '__linkto' in self._cw.form:
       
   156             if isinstance(self._cw.form['__linkto'], list):
       
   157                 # XXX which one should be considered (case: add a ticket to a
       
   158                 # version in jpl)
       
   159                 rtype, linkto_eid, role = self._cw.form['__linkto'][0].split(':')
       
   160             else:
       
   161                 rtype, linkto_eid, role = self._cw.form['__linkto'].split(':')
       
   162             linkto_rset = self._cw.eid_rset(linkto_eid)
       
   163             linkto_type = linkto_rset.description[0][0]
       
   164             if role == 'subject':
       
   165                 title = self._cw.__('creating %s (%s %s %s %%(linkto)s)' % (
       
   166                     entity.e_schema, entity.e_schema, rtype, linkto_type))
       
   167             else:
       
   168                 title = self._cw.__('creating %s (%s %%(linkto)s %s %s)' % (
       
   169                     entity.e_schema, linkto_type, rtype, entity.e_schema))
       
   170             msg = title % {'linkto' : self._cw.view('incontext', linkto_rset)}
       
   171             self.w(u'<div class="formTitle notransform"><span>%s</span></div>' % msg)
       
   172         else:
       
   173             super(CreationFormView, self).form_title(entity)
       
   174 
       
   175     def url(self):
       
   176         """return the url associated with this view"""
       
   177         req = self._cw
       
   178         return req.vreg["etypes"].etype_class(req.form['etype']).cw_create_url(
       
   179             req)
       
   180 
       
   181     def submited_message(self):
       
   182         """return the message that will be displayed on successful edition"""
       
   183         return self._cw._('entity created')
       
   184 
       
   185 
       
   186 class CopyFormView(EditionFormView):
       
   187     """display primary entity creation form initialized with values from another
       
   188     entity
       
   189     """
       
   190     __regid__ = 'copy'
       
   191 
       
   192     title = _('copy')
       
   193     warning_message = _('Please note that this is only a shallow copy')
       
   194 
       
   195     def render_form(self, entity):
       
   196         """fetch and render the form"""
       
   197         # make a copy of entity to avoid altering the entity in the
       
   198         # request's cache.
       
   199         entity.complete()
       
   200         self.newentity = copy(entity)
       
   201         self.copying = entity
       
   202         self.newentity.eid = next(self._cw.varmaker)
       
   203         self.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'
       
   204                % self._cw._(self.warning_message))
       
   205         super(CopyFormView, self).render_form(self.newentity)
       
   206         del self.newentity
       
   207 
       
   208     def init_form(self, form, entity):
       
   209         """customize your form before rendering here"""
       
   210         super(CopyFormView, self).init_form(form, entity)
       
   211         if entity.eid == self.newentity.eid:
       
   212             form.add_hidden(eid_param('__cloned_eid', entity.eid),
       
   213                             self.copying.eid)
       
   214         for rschema, role in form.editable_attributes():
       
   215             if not rschema.final:
       
   216                 # ensure relation cache is filed
       
   217                 rset = self.copying.related(rschema, role)
       
   218                 self.newentity.cw_set_relation_cache(rschema, role, rset)
       
   219 
       
   220     def submited_message(self):
       
   221         """return the message that will be displayed on successful edition"""
       
   222         return self._cw._('entity copied')
       
   223 
       
   224 
       
   225 class TableEditForm(forms.CompositeForm):
       
   226     __regid__ = 'muledit'
       
   227     domid = 'entityForm'
       
   228     onsubmit = "return validateForm('%s', null);" % domid
       
   229     form_buttons = [fw.SubmitButton(_('validate modifications on selected items')),
       
   230                     fw.ResetButton(_('revert changes'))]
       
   231 
       
   232     def __init__(self, req, rset, **kwargs):
       
   233         kwargs.setdefault('__redirectrql', rset.printable_rql())
       
   234         super(TableEditForm, self).__init__(req, rset=rset, **kwargs)
       
   235         for row in range(len(self.cw_rset)):
       
   236             form = self._cw.vreg['forms'].select('edition', self._cw,
       
   237                                                  rset=self.cw_rset, row=row,
       
   238                                                  formtype='muledit',
       
   239                                                  copy_nav_params=False,
       
   240                                                  mainform=False)
       
   241             # XXX rely on the EntityCompositeFormRenderer to put the eid input
       
   242             form.remove_field(form.field_by_name('eid'))
       
   243             self.add_subform(form)
       
   244 
       
   245 
       
   246 class TableEditFormView(FormViewMixIn, EntityView):
       
   247     __regid__ = 'muledit'
       
   248     __select__ = EntityView.__select__ & yes()
       
   249     title = _('multiple edit')
       
   250 
       
   251     def call(self, **kwargs):
       
   252         """a view to edit multiple entities of the same type the first column
       
   253         should be the eid
       
   254         """
       
   255         # XXX overriding formvid (eg __form_id) necessary to make work edition:
       
   256         # the edit controller try to select the form with no rset but
       
   257         # entity=entity, and use this form to edit the entity. So we want
       
   258         # edition form there but specifying formvid may have other undesired
       
   259         # side effect. Maybe we should provide another variable optionally
       
   260         # telling which form the edit controller should select (eg difffers
       
   261         # between html generation / post handling form)
       
   262         form = self._cw.vreg['forms'].select(self.__regid__, self._cw,
       
   263                                              rset=self.cw_rset,
       
   264                                              copy_nav_params=True,
       
   265                                              formvid='edition')
       
   266         form.render(w=self.w)
       
   267 
       
   268 
       
   269 # click and edit handling ('reledit') ##########################################
       
   270 
       
   271 ClickAndEditFormView = class_moved(reledit.ClickAndEditFormView)
       
   272 AutoClickAndEditFormView = class_moved(reledit.AutoClickAndEditFormView)