author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Sat, 01 Aug 2009 00:01:12 +0200 | |
changeset 2616 | 4501ee760eec |
parent 2613 | 5e19c2bb370e |
child 2650 | 18aec79ec3a3 |
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 |
2613
5e19c2bb370e
R [all] logilab.common 0.44 provides only deprecated
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2476
diff
changeset
|
13 |
from logilab.common.deprecation import deprecated |
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__() |
|
2099
0cba78f4fa12
[CWCache] yet another bugfix in cwcache: proper initalization of Cache.creation_date
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2098
diff
changeset
|
28 |
_now = datetime.now() |
0cba78f4fa12
[CWCache] yet another bugfix in cwcache: proper initalization of Cache.creation_date
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2098
diff
changeset
|
29 |
self.cache_creation_date = _now |
0cba78f4fa12
[CWCache] yet another bugfix in cwcache: proper initalization of Cache.creation_date
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2098
diff
changeset
|
30 |
self.latest_cache_lookup = _now |
1524 | 31 |
|
0 | 32 |
CACHE_REGISTRY = {} |
33 |
||
34 |
class AppRsetObject(VObject): |
|
35 |
"""This is the base class for CubicWeb application objects |
|
36 |
which are selected according to a request and result set. |
|
1524 | 37 |
|
0 | 38 |
Classes are kept in the vregistry and instantiation is done at selection |
39 |
time. |
|
1524 | 40 |
|
0 | 41 |
At registration time, the following attributes are set on the class: |
42 |
:vreg: |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
43 |
the instance's registry |
0 | 44 |
:schema: |
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
45 |
the instance's schema |
0 | 46 |
:config: |
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
47 |
the instance's configuration |
0 | 48 |
|
49 |
At instantiation time, the following attributes are set on the instance: |
|
50 |
:req: |
|
51 |
current request |
|
52 |
:rset: |
|
53 |
result set on which the object is applied |
|
54 |
""" |
|
805
6e99feeba28b
set a default selector on base app objects class
sylvain.thenault@logilab.fr
parents:
802
diff
changeset
|
55 |
__select__ = yes() |
0 | 56 |
|
57 |
@classmethod |
|
58 |
def registered(cls, vreg): |
|
721 | 59 |
super(AppRsetObject, cls).registered(vreg) |
0 | 60 |
cls.vreg = vreg |
61 |
cls.schema = vreg.schema |
|
62 |
cls.config = vreg.config |
|
63 |
cls.register_properties() |
|
64 |
return cls |
|
1524 | 65 |
|
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
|
66 |
@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
|
67 |
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
|
68 |
pass |
1524 | 69 |
|
0 | 70 |
@classmethod |
683 | 71 |
def selected(cls, *args, **kwargs): |
0 | 72 |
"""by default web app objects are usually instantiated on |
73 |
selection according to a request, a result set, and optional |
|
74 |
row and col |
|
75 |
""" |
|
875 | 76 |
assert len(args) <= 2 |
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
77 |
return cls(*args, **kwargs) |
0 | 78 |
|
79 |
# Eproperties definition: |
|
1398
5fe84a5f7035
rename internal entity types to have CW prefix instead of E
sylvain.thenault@logilab.fr
parents:
1282
diff
changeset
|
80 |
# key: id of the property (the actual CWProperty key is build using |
0 | 81 |
# <registry name>.<obj id>.<property id> |
82 |
# value: tuple (property type, vocabfunc, default value, property description) |
|
83 |
# possible types are those used by `logilab.common.configuration` |
|
84 |
# |
|
85 |
# notice that when it exists multiple objects with the same id (adaptation, |
|
86 |
# overriding) only the first encountered definition is considered, so those |
|
87 |
# objects can't try to have different default values for instance. |
|
1524 | 88 |
|
0 | 89 |
property_defs = {} |
1524 | 90 |
|
0 | 91 |
@classmethod |
92 |
def register_properties(cls): |
|
93 |
for propid, pdef in cls.property_defs.items(): |
|
94 |
pdef = pdef.copy() # may be shared |
|
95 |
pdef['default'] = getattr(cls, propid, pdef['default']) |
|
96 |
pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide')) |
|
97 |
cls.vreg.register_property(cls.propkey(propid), **pdef) |
|
1524 | 98 |
|
0 | 99 |
@classmethod |
100 |
def propkey(cls, propid): |
|
101 |
return '%s.%s.%s' % (cls.__registry__, cls.id, propid) |
|
699 | 102 |
|
103 |
@classproperty |
|
2613
5e19c2bb370e
R [all] logilab.common 0.44 provides only deprecated
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2476
diff
changeset
|
104 |
@deprecated('use __select__ and & or | operators') |
699 | 105 |
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
|
106 |
selector = cls.__select__ |
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
107 |
if isinstance(selector, AndSelector): |
732
45c3414ac002
__selectors__ must return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
726
diff
changeset
|
108 |
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
|
109 |
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
|
110 |
selector = (selector,) |
88a74f590986
improve __selectors__() so that it always return a tuple
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
721
diff
changeset
|
111 |
return selector |
1524 | 112 |
|
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
113 |
def __init__(self, req=None, rset=None, row=None, col=None, **extra): |
0 | 114 |
super(AppRsetObject, self).__init__() |
115 |
self.req = req |
|
116 |
self.rset = rset |
|
1143
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
117 |
self.row = row |
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
118 |
self.col = col |
8d097defbf2c
change the way selected/__init__ are used for AppRsetObject
sylvain.thenault@logilab.fr
parents:
1130
diff
changeset
|
119 |
self.extra_kwargs = extra |
1524 | 120 |
|
0 | 121 |
def get_cache(self, cachename): |
122 |
""" |
|
123 |
NOTE: cachename should be dotted names as in : |
|
124 |
- cubicweb.mycache |
|
1524 | 125 |
- cubes.blog.mycache |
0 | 126 |
- etc. |
127 |
""" |
|
128 |
if cachename in CACHE_REGISTRY: |
|
129 |
cache = CACHE_REGISTRY[cachename] |
|
130 |
else: |
|
2098
13aab3775af7
[CWCache] fix CWCache validity test
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2002
diff
changeset
|
131 |
cache = 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) |
|
2058
7ef12c03447c
nicer vreg api, try to make rset an optional named argument in select and derivated (including selectors)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2002
diff
changeset
|
154 |
nav = self.vreg.select_object('components', 'navigation', self.req, |
7ef12c03447c
nicer vreg api, try to make rset an optional named argument in select and derivated (including selectors)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2002
diff
changeset
|
155 |
rset=self.rset) |
0 | 156 |
if nav: |
157 |
start, stop = nav.page_boundaries() |
|
158 |
rql = self._limit_offset_rql(stop - start, start) |
|
159 |
# result set may have be limited manually in which case navigation won't |
|
160 |
# apply |
|
161 |
elif self.rset.limited: |
|
162 |
rql = self._limit_offset_rql(*self.rset.limited) |
|
163 |
# navigation component doesn't apply and rset has not been limited, no |
|
164 |
# need to limit query |
|
165 |
else: |
|
166 |
rql = self.rset.printable_rql() |
|
167 |
return rql |
|
1524 | 168 |
|
0 | 169 |
def _limit_offset_rql(self, limit, offset): |
170 |
rqlst = self.rset.syntax_tree() |
|
171 |
if len(rqlst.children) == 1: |
|
172 |
select = rqlst.children[0] |
|
173 |
olimit, ooffset = select.limit, select.offset |
|
174 |
select.limit, select.offset = limit, offset |
|
175 |
rql = rqlst.as_string(kwargs=self.rset.args) |
|
176 |
# restore original limit/offset |
|
177 |
select.limit, select.offset = olimit, ooffset |
|
178 |
else: |
|
179 |
newselect = Select() |
|
180 |
newselect.limit = limit |
|
181 |
newselect.offset = offset |
|
182 |
aliases = [VariableRef(newselect.get_variable(vref.name, i)) |
|
183 |
for i, vref in enumerate(rqlst.selection)] |
|
184 |
newselect.set_with([SubQuery(aliases, rqlst)], check=False) |
|
185 |
newunion = Union() |
|
186 |
newunion.append(newselect) |
|
187 |
rql = rqlst.as_string(kwargs=self.rset.args) |
|
188 |
rqlst.parent = None |
|
189 |
return rql |
|
1524 | 190 |
|
1144
654047cd0c30
move .view method on AppRsetObject, move initialize_varmaker method on View
sylvain.thenault@logilab.fr
parents:
1143
diff
changeset
|
191 |
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
|
192 |
"""shortcut to self.vreg.view method avoiding to pass self.req""" |
2058
7ef12c03447c
nicer vreg api, try to make rset an optional named argument in select and derivated (including selectors)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2002
diff
changeset
|
193 |
return self.vreg.render(__vid, self.req, __fallback_vid, rset=rset, |
7ef12c03447c
nicer vreg api, try to make rset an optional named argument in select and derivated (including selectors)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2002
diff
changeset
|
194 |
**kwargs) |
1524 | 195 |
|
1756
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
196 |
def initialize_varmaker(self): |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
197 |
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
|
198 |
if varmaker is None: |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
199 |
varmaker = self.req.varmaker |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
200 |
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
|
201 |
self.varmaker = varmaker |
42d87dedd631
move initialize_varmaker impl to base app object
sylvain.thenault@logilab.fr
parents:
1723
diff
changeset
|
202 |
|
0 | 203 |
# url generation methods ################################################## |
1524 | 204 |
|
0 | 205 |
controller = 'view' |
1524 | 206 |
|
2059
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
207 |
def build_url(self, *args, **kwargs): |
0 | 208 |
"""return an absolute URL using params dictionary key/values as URL |
209 |
parameters. Values are automatically URL quoted, and the |
|
210 |
publishing method to use may be specified or will be guessed. |
|
211 |
""" |
|
2059
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
212 |
# use *args since we don't want first argument to be "anonymous" to |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
213 |
# avoid potential clash with kwargs |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
214 |
if args: |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
215 |
assert len(args) == 1, 'only 0 or 1 non-named-argument expected' |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
216 |
method = args[0] |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
217 |
else: |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
218 |
method = None |
0 | 219 |
# XXX I (adim) think that if method is passed explicitly, we should |
220 |
# not try to process it and directly call req.build_url() |
|
221 |
if method is None: |
|
222 |
method = self.controller |
|
223 |
if method == 'view' and self.req.from_controller() == 'view' and \ |
|
224 |
not '_restpath' in kwargs: |
|
225 |
method = self.req.relative_path(includeparams=False) or 'view' |
|
226 |
return self.req.build_url(method, **kwargs) |
|
227 |
||
228 |
# various resources accessors ############################################# |
|
1524 | 229 |
|
0 | 230 |
def entity(self, row, col=0): |
231 |
"""short cut to get an entity instance for a particular row/column |
|
232 |
(col default to 0) |
|
233 |
""" |
|
234 |
return self.rset.get_entity(row, col) |
|
1524 | 235 |
|
0 | 236 |
def complete_entity(self, row, col=0, skip_bytes=True): |
237 |
"""short cut to get an completed entity instance for a particular |
|
238 |
row (all instance's attributes have been fetched) |
|
239 |
""" |
|
240 |
entity = self.entity(row, col) |
|
241 |
entity.complete(skip_bytes=skip_bytes) |
|
242 |
return entity |
|
243 |
||
244 |
def user_rql_callback(self, args, msg=None): |
|
245 |
"""register a user callback to execute some rql query and return an url |
|
246 |
to call it ready to be inserted in html |
|
247 |
""" |
|
248 |
def rqlexec(req, rql, args=None, key=None): |
|
249 |
req.execute(rql, args, key) |
|
250 |
return self.user_callback(rqlexec, args, msg) |
|
1524 | 251 |
|
0 | 252 |
def user_callback(self, cb, args, msg=None, nonify=False): |
253 |
"""register the given user callback and return an url to call it ready to be |
|
254 |
inserted in html |
|
255 |
""" |
|
1635
866563e2d0fc
don't depends on simplejson outside web/
sylvain.thenault@logilab.fr
parents:
1524
diff
changeset
|
256 |
from simplejson import dumps |
0 | 257 |
self.req.add_js('cubicweb.ajax.js') |
258 |
cbname = self.req.register_onetime_callback(cb, *args) |
|
1524 | 259 |
msg = dumps(msg or '') |
0 | 260 |
return "javascript:userCallbackThenReloadPage('%s', %s)" % ( |
261 |
cbname, msg) |
|
262 |
||
263 |
# formating methods ####################################################### |
|
264 |
||
265 |
def tal_render(self, template, variables): |
|
266 |
"""render a precompiled page template with variables in the given |
|
267 |
dictionary as context |
|
268 |
""" |
|
1637 | 269 |
from cubicweb.ext.tal import CubicWebContext |
0 | 270 |
context = CubicWebContext() |
271 |
context.update({'self': self, 'rset': self.rset, '_' : self.req._, |
|
272 |
'req': self.req, 'user': self.req.user}) |
|
273 |
context.update(variables) |
|
274 |
output = UStringIO() |
|
275 |
template.expand(context, output) |
|
276 |
return output.getvalue() |
|
277 |
||
278 |
def format_date(self, date, date_format=None, time=False): |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
279 |
"""return a string for a date time according to instance's |
0 | 280 |
configuration |
281 |
""" |
|
282 |
if date: |
|
283 |
if date_format is None: |
|
284 |
if time: |
|
285 |
date_format = self.req.property_value('ui.datetime-format') |
|
286 |
else: |
|
287 |
date_format = self.req.property_value('ui.date-format') |
|
288 |
return ustrftime(date, date_format) |
|
289 |
return u'' |
|
290 |
||
291 |
def format_time(self, time): |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
292 |
"""return a string for a time according to instance's |
0 | 293 |
configuration |
294 |
""" |
|
295 |
if time: |
|
296 |
return ustrftime(time, self.req.property_value('ui.time-format')) |
|
297 |
return u'' |
|
298 |
||
299 |
def format_float(self, num): |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2144
diff
changeset
|
300 |
"""return a string for floating point number according to instance's |
0 | 301 |
configuration |
302 |
""" |
|
303 |
if num: |
|
304 |
return self.req.property_value('ui.float-format') % num |
|
305 |
return u'' |
|
1524 | 306 |
|
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
307 |
def parse_datetime(self, value, etype='Datetime'): |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
308 |
"""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
|
309 |
Datetime formatted as Date are accepted |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
310 |
""" |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
311 |
assert etype in ('Datetime', 'Date', 'Time'), etype |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
312 |
# XXX raise proper validation error |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
313 |
if etype == 'Datetime': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
314 |
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
|
315 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
316 |
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
|
317 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
318 |
pass |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
319 |
elif etype == 'Time': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
320 |
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
|
321 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
322 |
# (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
|
323 |
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
|
324 |
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
|
325 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
326 |
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
|
327 |
try: |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
328 |
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
|
329 |
dt = strptime(value, format) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
330 |
if etype == 'Datetime': |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
331 |
return todatetime(dt) |
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
332 |
return todate(dt) |
2002
031414d88003
be stricter on catched exception, fix Time parsing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2001
diff
changeset
|
333 |
except ValueError: |
1984
8d571df6012a
move parse_datetime on appobject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
1977
diff
changeset
|
334 |
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
|
335 |
|
0 | 336 |
# security related methods ################################################ |
1524 | 337 |
|
0 | 338 |
def ensure_ro_rql(self, rql): |
339 |
"""raise an exception if the given rql is not a select query""" |
|
340 |
first = rql.split(' ', 1)[0].lower() |
|
341 |
if first in ('insert', 'set', 'delete'): |
|
342 |
raise Unauthorized(self.req._('only select queries are authorized')) |
|
343 |
||
1524 | 344 |
|
0 | 345 |
class AppObject(AppRsetObject): |
346 |
"""base class for application objects which are not selected |
|
347 |
according to a result set, only by their identifier. |
|
1524 | 348 |
|
0 | 349 |
Those objects may not have req, rset and cursor set. |
350 |
""" |
|
1524 | 351 |
|
0 | 352 |
@classmethod |
353 |
def selected(cls, *args, **kwargs): |
|
354 |
"""by default web app objects are usually instantiated on |
|
355 |
selection |
|
356 |
""" |
|
357 |
return cls(*args, **kwargs) |
|
358 |
||
359 |
def __init__(self, req=None, rset=None, **kwargs): |
|
360 |
self.req = req |
|
361 |
self.rset = rset |
|
362 |
self.__dict__.update(kwargs) |