|
1 """Set of HTML automatic forms to create, delete, copy or edit a single entity |
|
2 or a list of entities of the same type |
|
3 |
|
4 :organization: Logilab |
|
5 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
7 """ |
|
8 __docformat__ = "restructuredtext en" |
|
9 |
|
10 from copy import copy |
|
11 |
|
12 from simplejson import dumps |
|
13 |
|
14 from logilab.mtconverter import html_escape |
|
15 from logilab.common.decorators import cached |
|
16 |
|
17 from cubicweb.selectors import (specified_etype_implements, implements, |
|
18 match_kwargs, match_form_params, one_line_rset, |
|
19 non_final_entity, accepts_etype_compat) |
|
20 from cubicweb.utils import make_uid |
|
21 from cubicweb.view import View, EntityView |
|
22 from cubicweb.common import tags |
|
23 from cubicweb.common.uilib import cut |
|
24 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param |
|
25 from cubicweb.web.controller import NAV_FORM_PARAMETERS |
|
26 from cubicweb.web.form import MultipleFieldsForm, EntityFieldsForm, FormMixIn, FormRenderer |
|
27 from cubicweb.web.formfields import StringField, RichTextField, guess_field |
|
28 from cubicweb.web.formwidgets import HiddenInput |
|
29 |
|
30 _ = unicode |
|
31 |
|
32 |
|
33 class DeleteConfForm(EntityView): |
|
34 id = 'deleteconf' |
|
35 title = _('delete') |
|
36 domid = 'deleteconf' |
|
37 onsubmit = None |
|
38 # don't use navigation, all entities asked to be deleted should be displayed |
|
39 # else we will only delete the displayed page |
|
40 need_navigation = False |
|
41 |
|
42 def call(self): |
|
43 """ask for confirmation before real deletion""" |
|
44 req, w = self.req, self.w |
|
45 _ = req._ |
|
46 w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n' |
|
47 % _('this action is not reversible!')) |
|
48 # XXX above message should have style of a warning |
|
49 w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s) ?')) |
|
50 form = MultipleFieldsForm(req, domid='deleteconf', action=self.build_url('edit'), |
|
51 onsubmit=self.onsubmit, copy_nav_params=True) |
|
52 form.buttons.append(form.button_delete(label=stdmsgs.YES)) |
|
53 form.buttons.append(form.button_cancel(label=stdmsgs.NO)) |
|
54 done = set() |
|
55 w(u'<ul>\n') |
|
56 for i in xrange(self.rset.rowcount): |
|
57 if self.rset[i][0] in done: |
|
58 continue |
|
59 done.add(self.rset[i][0]) |
|
60 entity = self.rset.get_entity(i, 0) |
|
61 subform = EntityFieldsForm(req, set_error_url=False, |
|
62 entity=entity) |
|
63 form.form_add_subform(subform) |
|
64 # don't use outofcontext view or any other that may contain inline edition form |
|
65 w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'), |
|
66 href=entity.absolute_url())) |
|
67 w(u'</ul>\n') |
|
68 w(form.form_render()) |
|
69 |
|
70 |
|
71 class ClickAndEditForm(FormMixIn, EntityView): |
|
72 id = 'reledit' |
|
73 __select__ = non_final_entity() & match_kwargs('rtype') |
|
74 |
|
75 # FIXME editableField class could be toggleable from userprefs |
|
76 |
|
77 def cell_call(self, row, col, rtype=None, role='subject', reload=False): |
|
78 entity = self.entity(row, col) |
|
79 if getattr(entity, rtype) is None: |
|
80 value = self.req._('not specified') |
|
81 else: |
|
82 value = entity.printable_value(rtype) |
|
83 if not entity.has_perm('update'): |
|
84 self.w(value) |
|
85 return |
|
86 self.req.add_js( ('cubicweb.ajax.js',) ) |
|
87 eid = entity.eid |
|
88 edit_key = make_uid('%s-%s' % (rtype, eid)) |
|
89 divid = 'd%s' % edit_key |
|
90 reload = dumps(reload) |
|
91 buttons = [tags.input(klass="validateButton", type="submit", name="__action_apply", |
|
92 value=self.req._(stdmsgs.BUTTON_OK), tabindex=self.req.next_tabindex()), |
|
93 tags.input(klass="validateButton", type="button", |
|
94 value=self.req._(stdmsgs.BUTTON_CANCEL), |
|
95 onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" % (eid, rtype, divid), |
|
96 tabindex=self.req.next_tabindex())] |
|
97 form = self.vreg.select_object('forms', 'edition', self.req, self.rset, row=row, col=col, |
|
98 entity=entity, domid='%s-form' % divid, action='#', |
|
99 cssstyle='display: none', buttons=buttons, |
|
100 onsubmit="return inlineValidateForm('%(divid)s-form', '%(rtype)s', '%(eid)s', '%(divid)s', %(reload)s);" % locals()) |
|
101 renderer = FormRenderer(display_label=False, display_help=False, |
|
102 display_fields=(rtype,), button_bar_class='buttonbar') |
|
103 self.w(tags.div(value, klass='editableField', id=divid, |
|
104 ondblclick="showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')" % locals())) |
|
105 self.w(form.render(renderer=renderer)) |
|
106 |
|
107 |
|
108 class AutomaticEntityForm(EntityFieldsForm): |
|
109 id = 'edition' |
|
110 needs_js = EntityFieldsForm.needs_js + ('cubicweb.ajax.js',) |
|
111 |
|
112 def __init__(self, *args, **kwargs): |
|
113 super(AutomaticEntityForm, self).__init__(*args, **kwargs) |
|
114 self.entity.complete() |
|
115 for rschema, target in self.editable_attributes(entity): |
|
116 field = guess_field(entity.__class__, entity.e_schema, rschema, target) |
|
117 self.fields.append(field) |
|
118 |
|
119 def form_buttons(self): |
|
120 return [self.button_ok(tabindex=self.req.next_tabindex()), |
|
121 self.button_apply(tabindex=self.req.next_tabindex()), |
|
122 self.button_cancel(tabindex=self.req.next_tabindex())] |
|
123 |
|
124 def editable_attributes(self, entity): |
|
125 # XXX both (add, delete) required for non final relations |
|
126 return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add') |
|
127 if rschema != 'eid'] |
|
128 |
|
129 class _EditionForm(EntityView): |
|
130 """primary entity edition form |
|
131 |
|
132 When generating a new attribute_input, the editor will look for a method |
|
133 named 'default_ATTRNAME' on the entity instance, where ATTRNAME is the |
|
134 name of the attribute being edited. You may use this feature to compute |
|
135 dynamic default values such as the 'tomorrow' date or the user's login |
|
136 being connected |
|
137 """ |
|
138 id = 'edition' |
|
139 __select__ = one_line_rset() & non_final_entity() |
|
140 |
|
141 title = _('edition') |
|
142 controller = 'edit' |
|
143 skip_relations = FormMixIn.skip_relations.copy() |
|
144 |
|
145 def cell_call(self, row, col, **kwargs): |
|
146 self.req.add_js( ('cubicweb.ajax.js',) ) |
|
147 self.initialize_varmaker() |
|
148 entity = self.complete_entity(row, col) |
|
149 |
|
150 def initialize_varmaker(self): |
|
151 varmaker = self.req.get_page_data('rql_varmaker') |
|
152 if varmaker is None: |
|
153 varmaker = self.req.varmaker |
|
154 self.req.set_page_data('rql_varmaker', varmaker) |
|
155 self.varmaker = varmaker |
|
156 |
|
157 def edit_form(self, entity, kwargs): |
|
158 form = EntityFieldsForm(self.req, entity=entity) |
|
159 for rschema, target in self.editable_attributes(entity): |
|
160 field = guess_field(entity.__class__, entity.e_schema, rschema, target) |
|
161 form.fields.append(field) |
|
162 form.buttons.append(form.button_ok()) |
|
163 form.buttons.append(form.button_apply()) |
|
164 form.buttons.append(form.button_cancel()) |
|
165 self.w(form.form_render()) |
|
166 |
|
167 def editable_attributes(self, entity): |
|
168 # XXX both (add, delete) |
|
169 return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add') |
|
170 if rschema != 'eid'] |