5 :copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
5 :copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
7 """ |
7 """ |
8 __docformat__ = "restructuredtext en" |
8 __docformat__ = "restructuredtext en" |
9 |
9 |
10 from cubicweb.selectors import implements |
10 from logilab.mtconverter import html_escape |
|
11 |
|
12 from logilab.common.decorators import cached |
|
13 |
|
14 from cubicweb.selectors import (one_line_rset, none_rset, implements, |
|
15 match_user_groups, chainfirst, chainall) |
|
16 from cubicweb.common.utils import UStringIO |
|
17 from cubicweb.common.view import StartupView |
|
18 from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs |
11 from cubicweb.web.views import baseviews |
19 from cubicweb.web.views import baseviews |
|
20 from cubicweb.web.form import FormMixIn |
|
21 |
|
22 _ = unicode |
|
23 |
|
24 # some string we want to be internationalizable for nicer display of eproperty |
|
25 # groups |
|
26 _('navigation') |
|
27 _('ui') |
|
28 _('actions') |
|
29 _('boxes') |
|
30 _('components') |
|
31 _('contentnavigation') |
12 |
32 |
13 class EPropertyPrimaryView(baseviews.PrimaryView): |
33 class EPropertyPrimaryView(baseviews.PrimaryView): |
14 __selectors__ = implements('EProperty') |
34 __selectors__ = implements('EProperty') |
15 skip_none = False |
35 skip_none = False |
|
36 |
|
37 |
|
38 def make_togglable_link(nodeid, label, cookiename): |
|
39 """builds a HTML link that switches the visibility & remembers it""" |
|
40 action = u"javascript: toggle_and_remember_visibility('%s', '%s')" % \ |
|
41 (nodeid, cookiename) |
|
42 return u'<a href="%s">%s</a>' % (action, label) |
|
43 |
|
44 def css_class(someclass): |
|
45 return someclass and 'class="%s"' % someclass or '' |
|
46 |
|
47 class SystemEPropertiesForm(FormMixIn, StartupView): |
|
48 id = 'systemepropertiesform' |
|
49 __selectors__ = (none_rset, match_user_groups('managers')) |
|
50 |
|
51 title = _('site configuration') |
|
52 controller = 'edit' |
|
53 category = 'startupview' |
|
54 |
|
55 def linkable(self): |
|
56 return True |
|
57 |
|
58 def url(self): |
|
59 """return the url associated with this view. We can omit rql here""" |
|
60 return self.build_url('view', vid=self.id) |
|
61 |
|
62 def _cookie_name(self, somestr): |
|
63 return str('%s_property_%s' % (self.config.appid, somestr)) |
|
64 |
|
65 def _group_status(self, group, default=u'hidden'): |
|
66 cookies = self.req.get_cookie() |
|
67 cookiename = self._cookie_name(group) |
|
68 cookie = cookies.get(cookiename) |
|
69 if cookie is None: |
|
70 cookies[cookiename] = default |
|
71 self.req.set_cookie(cookies, cookiename, maxage=None) |
|
72 status = default |
|
73 else: |
|
74 status = cookie.value |
|
75 return status |
|
76 |
|
77 def call(self, **kwargs): |
|
78 """The default view representing the application's index""" |
|
79 self.req.add_js(('cubicweb.edition.js', 'cubicweb.preferences.js')) |
|
80 self.req.add_css('cubicweb.preferences.css') |
|
81 vreg = self.vreg |
|
82 values = self.defined_keys |
|
83 groupedopts = {} |
|
84 mainopts = {} |
|
85 # "self.id=='systemepropertiesform'" to skip site wide properties on |
|
86 # user's preference but not site's configuration |
|
87 for key in vreg.user_property_keys(self.id=='systemepropertiesform'): |
|
88 parts = key.split('.') |
|
89 if parts[0] in vreg: |
|
90 # appobject configuration |
|
91 reg, oid, propid = parts |
|
92 groupedopts.setdefault(reg, {}).setdefault(oid, []).append(key) |
|
93 else: |
|
94 mainopts.setdefault(parts[0], []).append(key) |
|
95 # precompute form to consume error message |
|
96 for group, keys in mainopts.items(): |
|
97 mainopts[group] = self.form(keys, False) |
|
98 for group, objects in groupedopts.items(): |
|
99 for oid, keys in objects.items(): |
|
100 groupedopts[group][oid] = self.form(keys, True) |
|
101 |
|
102 w = self.w |
|
103 req = self.req |
|
104 _ = req._ |
|
105 w(u'<h1>%s</h1>\n' % _(self.title)) |
|
106 w(self.error_message()) |
|
107 for label, group, form in sorted((_(g), g, f) |
|
108 for g, f in mainopts.iteritems()): |
|
109 status = css_class(self._group_status(group)) #'hidden' (collapsed), or '' (open) ? |
|
110 w(u'<h2 class="propertiesform">%s</h2>\n' % |
|
111 (make_togglable_link('fieldset_' + group, label, |
|
112 self._cookie_name(group)))) |
|
113 w(u'<div id="fieldset_%s" %s>' % (group, status)) |
|
114 w(u'<fieldset class="subentity">') |
|
115 w(form) |
|
116 w(u'</fieldset></div>') |
|
117 for label, group, objects in sorted((_(g), g, o) |
|
118 for g, o in groupedopts.iteritems()): |
|
119 status = css_class(self._group_status(group)) |
|
120 w(u'<h2 class="propertiesform">%s</h2>\n' % |
|
121 (make_togglable_link('fieldset_' + group, label, |
|
122 self._cookie_name(group)))) |
|
123 w(u'<div id="fieldset_%s" %s>' % (group, status)) |
|
124 for label, oid, form in sorted((self.req.__('%s_%s' % (group, o)), o, f) |
|
125 for o, f in objects.iteritems()): |
|
126 w(u'<fieldset class="subentity">') |
|
127 w(u'<legend class="componentTitle">%s</legend>\n' % label) |
|
128 docmsgid = '%s_%s_description' % (group, oid) |
|
129 doc = _(docmsgid) |
|
130 if doc != docmsgid: |
|
131 w(u'<p class="description">%s</p>' % html_escape(doc)) |
|
132 w(form) |
|
133 w(u'</fieldset>') |
|
134 w(u'</div>') |
|
135 |
|
136 @property |
|
137 @cached |
|
138 def eprops_rset(self): |
|
139 return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V, NOT P for_user U') |
|
140 |
|
141 @property |
|
142 def defined_keys(self): |
|
143 values = {} |
|
144 for i, entity in enumerate(self.eprops_rset.entities()): |
|
145 values[entity.pkey] = i |
|
146 return values |
|
147 |
|
148 def entity_for_key(self, key): |
|
149 values = self.defined_keys |
|
150 if key in values: |
|
151 entity = self.eprops_rset.get_entity(values[key], 0) |
|
152 else: |
|
153 entity = self.vreg.etype_class('EProperty')(self.req, None, None) |
|
154 entity.eid = self.req.varmaker.next() |
|
155 entity['value'] = self.vreg.property_value(key) |
|
156 return entity |
|
157 |
|
158 def form(self, keys, splitlabel=False): |
|
159 stream = UStringIO() |
|
160 w = stream.write |
|
161 w(u'<form action="%s" method="post">\n' % self.build_url()) |
|
162 w(u'<fieldset>\n') |
|
163 w(u'<input type="hidden" name="__errorurl" value="%s"/>\n' |
|
164 % html_escape(self.req.url())) |
|
165 w(u'<input type="hidden" name="__form_id" value="%s"/>\n' % self.id) |
|
166 path = self.req.relative_path() |
|
167 if '?' in path: |
|
168 path, params = path.split('?', 1) |
|
169 w(u'<input type="hidden" name="__redirectparams" value="%s"/>\n' |
|
170 % html_escape(params)) |
|
171 w(u'<input type="hidden" name="__redirectpath" value="%s"/>\n' % path) |
|
172 #w(u'<input type="hidden" name="__redirectrql" value=""/>\n') |
|
173 w(u'<input type="hidden" name="__message" value="%s"/>\n' |
|
174 % self.req._('changes applied')) |
|
175 w(u'<table><tr><td>\n') |
|
176 |
|
177 w(u'<table>\n') |
|
178 for key in keys: |
|
179 w(u'<tr>\n') |
|
180 self.form_row(w, key, splitlabel) |
|
181 w(u'</tr>\n') |
|
182 w(u'</table>\n') |
|
183 w(u'</td></tr><tr><td>\n') |
|
184 w(self.button_ok()) |
|
185 w(self.button_cancel()) |
|
186 w(u'</td></tr></table>\n') |
|
187 w(u'</fieldset>\n') |
|
188 w(u'</form>\n') |
|
189 return stream.getvalue() |
|
190 |
|
191 def form_row(self, w, key, splitlabel): |
|
192 entity = self.entity_for_key(key) |
|
193 eid = entity.eid |
|
194 if splitlabel: |
|
195 w(u'<td class="label">%s</td>' % self.req._(key.split('.')[-1])) |
|
196 else: |
|
197 w(u'<td class="label">%s</td>' % self.req._(key)) |
|
198 wdg = self.vreg.property_value_widget(key, req=self.req) |
|
199 error = wdg.render_error(entity) |
|
200 w(u'<td class="%s">' % (error and 'error' or '')) |
|
201 w(error) |
|
202 self.form_row_hiddens(w, entity, key) |
|
203 w(wdg.edit_render(entity)) |
|
204 w(u'</td>\n') |
|
205 w(u'<td>%s</td>' % wdg.render_help(entity)) |
|
206 return entity |
|
207 |
|
208 def form_row_hiddens(self, w, entity, key): |
|
209 eid = entity.eid |
|
210 w(u'<input type="hidden" name="eid" value="%s"/>' % eid) |
|
211 w(u'<input type="hidden" name="%s" value="EProperty"/>' % eid_param('__type', eid)) |
|
212 w(u'<input type="hidden" name="%s" value="%s"/>' % (eid_param('pkey', eid), key)) |
|
213 w(u'<input type="hidden" name="%s" value="%s"/>' % (eid_param('edits-pkey', eid), '')) |
|
214 |
|
215 |
|
216 |
|
217 def is_user_prefs(cls, req, rset, row, col): |
|
218 return req.user.eid == rset[row or 0 ][col or 0] |
|
219 |
|
220 |
|
221 class EPropertiesForm(SystemEPropertiesForm): |
|
222 id = 'epropertiesform' |
|
223 __selectors__ = ( |
|
224 implements('EUser'), |
|
225 # we don't want guests to be able to come here |
|
226 match_user_groups('users', 'managers'), |
|
227 chainfirst(none_rset), |
|
228 chainall(one_line_rset, is_user_prefs), |
|
229 chainall(one_line_rset, match_user_groups('managers')) |
|
230 ) |
|
231 |
|
232 title = _('preferences') |
|
233 |
|
234 @property |
|
235 def user(self): |
|
236 if self.rset is None: |
|
237 return self.req.user |
|
238 return self.rset.get_entity(self.row or 0, self.col or 0) |
|
239 |
|
240 @property |
|
241 @cached |
|
242 def eprops_rset(self): |
|
243 return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V,' |
|
244 'P for_user U, U eid %(x)s', {'x': self.user.eid}) |
|
245 |
|
246 def form_row_hiddens(self, w, entity, key): |
|
247 super(EPropertiesForm, self).form_row_hiddens(w, entity, key) |
|
248 # if user is in the managers group and the property is being created, |
|
249 # we have to set for_user explicitly |
|
250 if not entity.has_eid() and self.user.matching_groups('managers'): |
|
251 eid = entity.eid |
|
252 w(u'<input type="hidden" name="%s" value="%s"/>' |
|
253 % (eid_param('edits-for_user', eid), INTERNAL_FIELD_VALUE)) |
|
254 w(u'<input type="hidden" name="%s" value="%s"/>' |
|
255 % (eid_param('for_user', eid), self.user.eid)) |
|
256 |