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) |
|