author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Tue, 09 Jun 2009 18:04:31 +0200 | |
branch | stable |
changeset 2075 | 933ccfce6e91 |
parent 2002 | 031414d88003 |
child 2058 | 7ef12c03447c |
child 2098 | 13aab3775af7 |
permissions | -rw-r--r-- |
0 | 1 |
"""Base class for dynamically loaded objects manipulated in the web interface |
2 |
||
3 |
:organization: Logilab |
|
1977
606923dff11b
big bunch of copyright / docstring update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1756
diff
changeset
|
4 |
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
0 | 5 |
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
1977
606923dff11b
big bunch of copyright / docstring update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1756
diff
changeset
|
6 |
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
0 | 7 |
""" |
8 |
__docformat__ = "restructuredtext en" |
|
9 |
||
2002
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
10 |
from datetime import datetime, timedelta, time |
0 | 11 |
|
699 | 12 |
from logilab.common.decorators import classproperty |
0 | 13 |
from logilab.common.deprecation import obsolete |
447 | 14 |
|
15 |
from rql.nodes import VariableRef, SubQuery |
|
0 | 16 |
from rql.stmts import Union, Select |
17 |
||
1174 | 18 |
from cubicweb import Unauthorized, NoSelectableObject |
726
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
19 |
from cubicweb.vregistry import VObject, AndSelector |
692
800592b8d39b
replace deprecated cubicweb.common.selectors by its new module path (cubicweb.selectors)
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
683
diff
changeset
|
20 |
from cubicweb.selectors import yes |
2001 | 21 |
from cubicweb.utils import UStringIO, ustrftime, strptime, todate, todatetime |
0 | 22 |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
23 |
ONESECOND = timedelta(0, 1, 0) |
0 | 24 |
|
1524 | 25 |
class Cache(dict): |
0 | 26 |
def __init__(self): |
27 |
super(Cache, self).__init__() |
|
28 |
self.cache_creation_date = None |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
29 |
self.latest_cache_lookup = datetime.now() |
1524 | 30 |
|
0 | 31 |
CACHE_REGISTRY = {} |
32 |
||
33 |
class AppRsetObject(VObject): |
|
34 |
"""This is the base class for CubicWeb application objects |
|
35 |
which are selected according to a request and result set. |
|
1524 | 36 |
|
0 | 37 |
Classes are kept in the vregistry and instantiation is done at selection |
38 |
time. |
|
1524 | 39 |
|
0 | 40 |
At registration time, the following attributes are set on the class: |
41 |
:vreg: |
|
42 |
the application's registry |
|
43 |
:schema: |
|
44 |
the application's schema |
|
45 |
:config: |
|
46 |
the application's configuration |
|
47 |
||
48 |
At instantiation time, the following attributes are set on the instance: |
|
49 |
:req: |
|
50 |
current request |
|
51 |
:rset: |
|
52 |
result set on which the object is applied |
|
53 |
""" |
|
805
6e99feeba28b
set a default selector on base app objects class
sylvain.thenault@logilab.fr
parents:
802
diff
changeset
|
54 |
__select__ = yes() |
0 | 55 |
|
56 |
@classmethod |
|
57 |
def registered(cls, vreg): |
|
721 | 58 |
super(AppRsetObject, cls).registered(vreg) |
0 | 59 |
cls.vreg = vreg |
60 |
cls.schema = vreg.schema |
|
61 |
cls.config = vreg.config |
|
62 |
cls.register_properties() |
|
63 |
return cls |
|
1524 | 64 |
|
1282
272d8ec6f308
* print vreg content once fully initialized (require move of print code from vregistry to cwvreg)
sylvain.thenault@logilab.fr
parents:
1174
diff
changeset
|
65 |
@classmethod |
272d8ec6f308
* print vreg content once fully initialized (require move of print code from vregistry to cwvreg)
sylvain.thenault@logilab.fr
parents:
1174
diff
changeset
|
66 |
def vreg_initialization_completed(cls): |
272d8ec6f308
* print vreg content once fully initialized (require move of print code from vregistry to cwvreg)
sylvain.thenault@logilab.fr
parents:
1174
diff
changeset
|
67 |
pass |
1524 | 68 |
|
0 | 69 |
@classmethod |
683 | 70 |
def selected(cls, *args, **kwargs): |
0 | 71 |
"""by default web app objects are usually instantiated on |
72 |
selection according to a request, a result set, and optional |
|
73 |
row and col |
|
74 |
""" |
|
875 | 75 |
assert len(args) <= 2 |
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
76 |
return cls(*args, **kwargs) |
0 | 77 |
|
78 |
# Eproperties definition: |
|
1398
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1282
diff
changeset
|
79 |
# key: id of the property (the actual CWProperty key is build using |
0 | 80 |
# <registry name>.<obj id>.<property id> |
81 |
# value: tuple (property type, vocabfunc, default value, property description) |
|
82 |
# possible types are those used by `logilab.common.configuration` |
|
83 |
# |
|
84 |
# notice that when it exists multiple objects with the same id (adaptation, |
|
85 |
# overriding) only the first encountered definition is considered, so those |
|
86 |
# objects can't try to have different default values for instance. |
|
1524 | 87 |
|
0 | 88 |
property_defs = {} |
1524 | 89 |
|
0 | 90 |
@classmethod |
91 |
def register_properties(cls): |
|
92 |
for propid, pdef in cls.property_defs.items(): |
|
93 |
pdef = pdef.copy() # may be shared |
|
94 |
pdef['default'] = getattr(cls, propid, pdef['default']) |
|
95 |
pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide')) |
|
96 |
cls.vreg.register_property(cls.propkey(propid), **pdef) |
|
1524 | 97 |
|
0 | 98 |
@classmethod |
99 |
def propkey(cls, propid): |
|
100 |
return '%s.%s.%s' % (cls.__registry__, cls.id, propid) |
|
699 | 101 |
|
102 |
@classproperty |
|
103 |
@obsolete('use __select__ and & or | operators') |
|
104 |
def __selectors__(cls): |
|
726
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
105 |
selector = cls.__select__ |
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
106 |
if isinstance(selector, AndSelector): |
732
45c3414ac002
__selectors__ must return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
726
diff
changeset
|
107 |
return tuple(selector.selectors) |
726
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
108 |
if not isinstance(selector, tuple): |
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
109 |
selector = (selector,) |
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
110 |
return selector |
1524 | 111 |
|
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
112 |
def __init__(self, req=None, rset=None, row=None, col=None, **extra): |
0 | 113 |
super(AppRsetObject, self).__init__() |
114 |
self.req = req |
|
115 |
self.rset = rset |
|
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
116 |
self.row = row |
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
117 |
self.col = col |
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
118 |
self.extra_kwargs = extra |
1524 | 119 |
|
0 | 120 |
def get_cache(self, cachename): |
121 |
""" |
|
122 |
NOTE: cachename should be dotted names as in : |
|
123 |
- cubicweb.mycache |
|
1524 | 124 |
- cubes.blog.mycache |
0 | 125 |
- etc. |
126 |
""" |
|
127 |
if cachename in CACHE_REGISTRY: |
|
128 |
cache = CACHE_REGISTRY[cachename] |
|
129 |
else: |
|
130 |
cache = Cache() |
|
131 |
CACHE_REGISTRY[cachename] = cache |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
132 |
_now = datetime.now() |
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
133 |
if _now > cache.latest_cache_lookup + ONESECOND: |
1524 | 134 |
ecache = self.req.execute('Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T', |
0 | 135 |
{'name':cachename}).get_entity(0,0) |
136 |
cache.latest_cache_lookup = _now |
|
137 |
if not ecache.valid(cache.cache_creation_date): |
|
1130
17ff4d4bfbd0
should call clear, not empth (thanks pylint)
sylvain.thenault@logilab.fr
parents:
1129
diff
changeset
|
138 |
cache.clear() |
0 | 139 |
cache.cache_creation_date = _now |
140 |
return cache |
|
141 |
||
142 |
def propval(self, propid): |
|
143 |
assert self.req |
|
144 |
return self.req.property_value(self.propkey(propid)) |
|
1524 | 145 |
|
0 | 146 |
def limited_rql(self): |
147 |
"""return a printable rql for the result set associated to the object, |
|
148 |
with limit/offset correctly set according to maximum page size and |
|
149 |
currently displayed page when necessary |
|
150 |
""" |
|
151 |
# try to get page boundaries from the navigation component |
|
152 |
# XXX we should probably not have a ref to this component here (eg in |
|
153 |
# cubicweb.common) |
|
154 |
nav = self.vreg.select_component('navigation', self.req, self.rset) |
|
155 |
if nav: |
|
156 |
start, stop = nav.page_boundaries() |
|
157 |
rql = self._limit_offset_rql(stop - start, start) |
|
158 |
# result set may have be limited manually in which case navigation won't |
|
159 |
# apply |
|
160 |
elif self.rset.limited: |
|
161 |
rql = self._limit_offset_rql(*self.rset.limited) |
|
162 |
# navigation component doesn't apply and rset has not been limited, no |
|
163 |
# need to limit query |
|
164 |
else: |
|
165 |
rql = self.rset.printable_rql() |
|
166 |
return rql |
|
1524 | 167 |
|
0 | 168 |
def _limit_offset_rql(self, limit, offset): |
169 |
rqlst = self.rset.syntax_tree() |
|
170 |
if len(rqlst.children) == 1: |
|
171 |
select = rqlst.children[0] |
|
172 |
olimit, ooffset = select.limit, select.offset |
|
173 |
select.limit, select.offset = limit, offset |
|
174 |
rql = rqlst.as_string(kwargs=self.rset.args) |
|
175 |
# restore original limit/offset |
|
176 |
select.limit, select.offset = olimit, ooffset |
|
177 |
else: |
|
178 |
newselect = Select() |
|
179 |
newselect.limit = limit |
|
180 |
newselect.offset = offset |
|
181 |
aliases = [VariableRef(newselect.get_variable(vref.name, i)) |
|
182 |
for i, vref in enumerate(rqlst.selection)] |
|
183 |
newselect.set_with([SubQuery(aliases, rqlst)], check=False) |
|
184 |
newunion = Union() |
|
185 |
newunion.append(newselect) |
|
186 |
rql = rqlst.as_string(kwargs=self.rset.args) |
|
187 |
rqlst.parent = None |
|
188 |
return rql |
|
1524 | 189 |
|
1144
654047cd0c30
move .view method on AppRsetObject, move initialize_varmaker method on View
sylvain.thenault@logilab.fr
parents:
1143
diff
changeset
|
190 |
def view(self, __vid, rset=None, __fallback_vid=None, **kwargs): |
1985
9c1db4e06095
move view method's logic on the registry, so it's easier to call it from outside an appobject. Also make rset argument optional
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1984
diff
changeset
|
191 |
"""shortcut to self.vreg.view method avoiding to pass self.req""" |
9c1db4e06095
move view method's logic on the registry, so it's easier to call it from outside an appobject. Also make rset argument optional
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1984
diff
changeset
|
192 |
return self.vreg.view(__vid, self.req, rset, __fallback_vid, **kwargs) |
1524 | 193 |
|
1756
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
194 |
def initialize_varmaker(self): |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
195 |
varmaker = self.req.get_page_data('rql_varmaker') |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
196 |
if varmaker is None: |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
197 |
varmaker = self.req.varmaker |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
198 |
self.req.set_page_data('rql_varmaker', varmaker) |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
199 |
self.varmaker = varmaker |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
200 |
|
0 | 201 |
# url generation methods ################################################## |
1524 | 202 |
|
0 | 203 |
controller = 'view' |
1524 | 204 |
|
0 | 205 |
def build_url(self, method=None, **kwargs): |
206 |
"""return an absolute URL using params dictionary key/values as URL |
|
207 |
parameters. Values are automatically URL quoted, and the |
|
208 |
publishing method to use may be specified or will be guessed. |
|
209 |
""" |
|
210 |
# XXX I (adim) think that if method is passed explicitly, we should |
|
211 |
# not try to process it and directly call req.build_url() |
|
212 |
if method is None: |
|
213 |
method = self.controller |
|
214 |
if method == 'view' and self.req.from_controller() == 'view' and \ |
|
215 |
not '_restpath' in kwargs: |
|
216 |
method = self.req.relative_path(includeparams=False) or 'view' |
|
217 |
return self.req.build_url(method, **kwargs) |
|
218 |
||
219 |
# various resources accessors ############################################# |
|
1524 | 220 |
|
0 | 221 |
def entity(self, row, col=0): |
222 |
"""short cut to get an entity instance for a particular row/column |
|
223 |
(col default to 0) |
|
224 |
""" |
|
225 |
return self.rset.get_entity(row, col) |
|
1524 | 226 |
|
0 | 227 |
def complete_entity(self, row, col=0, skip_bytes=True): |
228 |
"""short cut to get an completed entity instance for a particular |
|
229 |
row (all instance's attributes have been fetched) |
|
230 |
""" |
|
231 |
entity = self.entity(row, col) |
|
232 |
entity.complete(skip_bytes=skip_bytes) |
|
233 |
return entity |
|
234 |
||
235 |
def user_rql_callback(self, args, msg=None): |
|
236 |
"""register a user callback to execute some rql query and return an url |
|
237 |
to call it ready to be inserted in html |
|
238 |
""" |
|
239 |
def rqlexec(req, rql, args=None, key=None): |
|
240 |
req.execute(rql, args, key) |
|
241 |
return self.user_callback(rqlexec, args, msg) |
|
1524 | 242 |
|
0 | 243 |
def user_callback(self, cb, args, msg=None, nonify=False): |
244 |
"""register the given user callback and return an url to call it ready to be |
|
245 |
inserted in html |
|
246 |
""" |
|
1635
866563e2d0fc
don't depends on simplejson outside web/
sylvain.thenault@logilab.fr
parents:
1524
diff
changeset
|
247 |
from simplejson import dumps |
0 | 248 |
self.req.add_js('cubicweb.ajax.js') |
249 |
cbname = self.req.register_onetime_callback(cb, *args) |
|
1524 | 250 |
msg = dumps(msg or '') |
0 | 251 |
return "javascript:userCallbackThenReloadPage('%s', %s)" % ( |
252 |
cbname, msg) |
|
253 |
||
254 |
# formating methods ####################################################### |
|
255 |
||
256 |
def tal_render(self, template, variables): |
|
257 |
"""render a precompiled page template with variables in the given |
|
258 |
dictionary as context |
|
259 |
""" |
|
1637 | 260 |
from cubicweb.ext.tal import CubicWebContext |
0 | 261 |
context = CubicWebContext() |
262 |
context.update({'self': self, 'rset': self.rset, '_' : self.req._, |
|
263 |
'req': self.req, 'user': self.req.user}) |
|
264 |
context.update(variables) |
|
265 |
output = UStringIO() |
|
266 |
template.expand(context, output) |
|
267 |
return output.getvalue() |
|
268 |
||
269 |
def format_date(self, date, date_format=None, time=False): |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
270 |
"""return a string for a date time according to application's |
0 | 271 |
configuration |
272 |
""" |
|
273 |
if date: |
|
274 |
if date_format is None: |
|
275 |
if time: |
|
276 |
date_format = self.req.property_value('ui.datetime-format') |
|
277 |
else: |
|
278 |
date_format = self.req.property_value('ui.date-format') |
|
279 |
return ustrftime(date, date_format) |
|
280 |
return u'' |
|
281 |
||
282 |
def format_time(self, time): |
|
1016
26387b836099
use datetime instead of mx.DateTime
sylvain.thenault@logilab.fr
parents:
875
diff
changeset
|
283 |
"""return a string for a time according to application's |
0 | 284 |
configuration |
285 |
""" |
|
286 |
if time: |
|
287 |
return ustrftime(time, self.req.property_value('ui.time-format')) |
|
288 |
return u'' |
|
289 |
||
290 |
def format_float(self, num): |
|
291 |
"""return a string for floating point number according to application's |
|
292 |
configuration |
|
293 |
""" |
|
294 |
if num: |
|
295 |
return self.req.property_value('ui.float-format') % num |
|
296 |
return u'' |
|
1524 | 297 |
|
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
298 |
def parse_datetime(self, value, etype='Datetime'): |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
299 |
"""get a datetime or time from a string (according to etype) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
300 |
Datetime formatted as Date are accepted |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
301 |
""" |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
302 |
assert etype in ('Datetime', 'Date', 'Time'), etype |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
303 |
# XXX raise proper validation error |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
304 |
if etype == 'Datetime': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
305 |
format = self.req.property_value('ui.datetime-format') |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
306 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
307 |
return todatetime(strptime(value, format)) |
2002
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
308 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
309 |
pass |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
310 |
elif etype == 'Time': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
311 |
format = self.req.property_value('ui.time-format') |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
312 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
313 |
# (adim) I can't find a way to parse a Time with a custom format |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
314 |
date = strptime(value, format) # this returns a DateTime |
2002
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
315 |
return time(date.hour, date.minute, date.second) |
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
316 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
317 |
raise ValueError('can\'t parse %r (expected %s)' % (value, format)) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
318 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
319 |
format = self.req.property_value('ui.date-format') |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
320 |
dt = strptime(value, format) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
321 |
if etype == 'Datetime': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
322 |
return todatetime(dt) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
323 |
return todate(dt) |
2002
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
324 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
325 |
raise ValueError('can\'t parse %r (expected %s)' % (value, format)) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
326 |
|
0 | 327 |
# security related methods ################################################ |
1524 | 328 |
|
0 | 329 |
def ensure_ro_rql(self, rql): |
330 |
"""raise an exception if the given rql is not a select query""" |
|
331 |
first = rql.split(' ', 1)[0].lower() |
|
332 |
if first in ('insert', 'set', 'delete'): |
|
333 |
raise Unauthorized(self.req._('only select queries are authorized')) |
|
334 |
||
1524 | 335 |
|
0 | 336 |
class AppObject(AppRsetObject): |
337 |
"""base class for application objects which are not selected |
|
338 |
according to a result set, only by their identifier. |
|
1524 | 339 |
|
0 | 340 |
Those objects may not have req, rset and cursor set. |
341 |
""" |
|
1524 | 342 |
|
0 | 343 |
@classmethod |
344 |
def selected(cls, *args, **kwargs): |
|
345 |
"""by default web app objects are usually instantiated on |
|
346 |
selection |
|
347 |
""" |
|
348 |
return cls(*args, **kwargs) |
|
349 |
||
350 |
def __init__(self, req=None, rset=None, **kwargs): |
|
351 |
self.req = req |
|
352 |
self.rset = rset |
|
353 |
self.__dict__.update(kwargs) |