author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Wed, 14 Apr 2010 17:31:41 +0200 | |
changeset 5250 | 1c0eb5f74fd4 |
parent 5244 | 5467674ad101 |
child 5302 | dfd147de06b2 |
permissions | -rw-r--r-- |
0 | 1 |
"""abstract class for http request |
2 |
||
3 |
:organization: Logilab |
|
4212
ab6573088b4a
update copyright: welcome 2010
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3838
diff
changeset
|
4 |
:copyright: 2001-2010 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:
1801
diff
changeset
|
6 |
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
0 | 7 |
""" |
8 |
__docformat__ = "restructuredtext en" |
|
9 |
||
10 |
import Cookie |
|
11 |
import sha |
|
12 |
import time |
|
13 |
import random |
|
14 |
import base64 |
|
3653
ef71abb1e77b
[req] new expires argument to set_cookie
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3293
diff
changeset
|
15 |
from datetime import date |
0 | 16 |
from urlparse import urlsplit |
17 |
from itertools import count |
|
18 |
||
2792
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
19 |
from simplejson import dumps |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
20 |
|
0 | 21 |
from rql.utils import rqlvar_maker |
22 |
||
23 |
from logilab.common.decorators import cached |
|
2613
5e19c2bb370e
R [all] logilab.common 0.44 provides only deprecated
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2559
diff
changeset
|
24 |
from logilab.common.deprecation import deprecated |
2312
af4d8f75c5db
use xml_escape
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2258
diff
changeset
|
25 |
from logilab.mtconverter import xml_escape |
1801
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
26 |
|
0 | 27 |
from cubicweb.dbapi import DBAPIRequest |
4023
eae23c40627a
drop common subpackage
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4004
diff
changeset
|
28 |
from cubicweb.mail import header |
eae23c40627a
drop common subpackage
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4004
diff
changeset
|
29 |
from cubicweb.uilib import remove_html_tags |
3816
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
30 |
from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid |
3094
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
31 |
from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT |
1801
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
32 |
from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
33 |
RequestError, StatusResponse) |
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
34 |
from cubicweb.web.http_headers import Headers |
0 | 35 |
|
662
6f867ab70e3d
move _MARKER from appobject to web.request
sylvain.thenault@logilab.fr
parents:
610
diff
changeset
|
36 |
_MARKER = object() |
6f867ab70e3d
move _MARKER from appobject to web.request
sylvain.thenault@logilab.fr
parents:
610
diff
changeset
|
37 |
|
0 | 38 |
|
39 |
def list_form_param(form, param, pop=False): |
|
40 |
"""get param from form parameters and return its value as a list, |
|
41 |
skipping internal markers if any |
|
42 |
||
43 |
* if the parameter isn't defined, return an empty list |
|
44 |
* if the parameter is a single (unicode) value, return a list |
|
45 |
containing that value |
|
46 |
* if the parameter is already a list or tuple, just skip internal |
|
47 |
markers |
|
48 |
||
49 |
if pop is True, the parameter is removed from the form dictionnary |
|
50 |
""" |
|
51 |
if pop: |
|
52 |
try: |
|
53 |
value = form.pop(param) |
|
54 |
except KeyError: |
|
55 |
return [] |
|
56 |
else: |
|
57 |
value = form.get(param, ()) |
|
58 |
if value is None: |
|
59 |
value = () |
|
60 |
elif not isinstance(value, (list, tuple)): |
|
61 |
value = [value] |
|
62 |
return [v for v in value if v != INTERNAL_FIELD_VALUE] |
|
63 |
||
64 |
||
65 |
||
66 |
class CubicWebRequestBase(DBAPIRequest): |
|
1421
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
67 |
"""abstract HTTP request, should be extended according to the HTTP backend""" |
2255
c346af0727ca
more generic way to detect json requests (not yet perfect though)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2245
diff
changeset
|
68 |
json_request = False # to be set to True by json controllers |
1421
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
69 |
|
0 | 70 |
def __init__(self, vreg, https, form=None): |
71 |
super(CubicWebRequestBase, self).__init__(vreg) |
|
72 |
self.authmode = vreg.config['auth-mode'] |
|
73 |
self.https = https |
|
74 |
# raw html headers that can be added from any view |
|
75 |
self.html_headers = HTMLHead() |
|
76 |
# form parameters |
|
77 |
self.setup_params(form) |
|
78 |
# dictionnary that may be used to store request data that has to be |
|
79 |
# shared among various components used to publish the request (views, |
|
80 |
# controller, application...) |
|
81 |
self.data = {} |
|
82 |
# search state: 'normal' or 'linksearch' (eg searching for an object |
|
83 |
# to create a relation with another) |
|
1426 | 84 |
self.search_state = ('normal',) |
0 | 85 |
# tabindex generator |
2556 | 86 |
self.tabindexgen = count(1) |
0 | 87 |
self.next_tabindex = self.tabindexgen.next |
88 |
# page id, set by htmlheader template |
|
89 |
self.pageid = None |
|
90 |
self.datadir_url = self._datadir_url() |
|
3816
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
91 |
self._set_pageid() |
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
92 |
# prepare output header |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
93 |
self.headers_out = Headers() |
3816
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
94 |
|
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
95 |
def _set_pageid(self): |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
96 |
"""initialize self.pageid |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
97 |
if req.form provides a specific pageid, use it, otherwise build a |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
98 |
new one. |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
99 |
""" |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
100 |
pid = self.form.get('pageid') |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
101 |
if pid is None: |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
102 |
pid = make_uid(id(self)) |
37b376bb4088
[web] set pageid at request instanciation rather than in htmlheader template
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3275
diff
changeset
|
103 |
self.pageid = pid |
3838
9cc134372bf8
[web] safety belt to avoid overriding pageid with loadxhtml()
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3816
diff
changeset
|
104 |
self.html_headers.define_var('pageid', pid, override=False) |
0 | 105 |
|
2792
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
106 |
@property |
2801
7ef4c1c9266b
fix syntax error
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2792
diff
changeset
|
107 |
def varmaker(self): |
4366
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
108 |
"""the rql varmaker is exposed both as a property and as the |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
109 |
set_varmaker function since we've two use cases: |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
110 |
|
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
111 |
* accessing the req.varmaker property to get a new variable name |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
112 |
|
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
113 |
* calling req.set_varmaker() to ensure a varmaker is set for later ajax |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
114 |
calls sharing our .pageid |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
115 |
""" |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
116 |
return self.set_varmaker() |
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
117 |
|
d51f28ba9399
fif inlined relation forms pb w/ new ajax forms.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4277
diff
changeset
|
118 |
def set_varmaker(self): |
2792
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
119 |
varmaker = self.get_page_data('rql_varmaker') |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
120 |
if varmaker is None: |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
121 |
varmaker = rqlvar_maker() |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
122 |
self.set_page_data('rql_varmaker', varmaker) |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
123 |
return varmaker |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
124 |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
125 |
def set_session(self, session, user=None): |
0 | 126 |
"""method called by the session handler when the user is authenticated |
127 |
or an anonymous connection is open |
|
128 |
""" |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
129 |
super(CubicWebRequestBase, self).set_session(session, user) |
2245
7463e1a748dd
new set_session_props method exposed by the repository, use it to be sure session language is in sync the request language
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2202
diff
changeset
|
130 |
# set request language |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
131 |
vreg = self.vreg |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
132 |
if self.user: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
133 |
try: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
134 |
# 1. user specified language |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
135 |
lang = vreg.typed_value('ui.language', |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
136 |
self.user.properties['ui.language']) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
137 |
self.set_language(lang) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
138 |
return |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
139 |
except KeyError: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
140 |
pass |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
141 |
if vreg.config['language-negociation']: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
142 |
# 2. http negociated language |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
143 |
for lang in self.header_accept_language(): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
144 |
if lang in self.translations: |
0 | 145 |
self.set_language(lang) |
146 |
return |
|
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
147 |
# 3. default language |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
148 |
self.set_default_language(vreg) |
1426 | 149 |
|
0 | 150 |
def set_language(self, lang): |
3275
5247789df541
[gettext] provide GNU contexts to avoid translations ambiguities
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3094
diff
changeset
|
151 |
gettext, self.pgettext = self.translations[lang] |
5247789df541
[gettext] provide GNU contexts to avoid translations ambiguities
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
3094
diff
changeset
|
152 |
self._ = self.__ = gettext |
0 | 153 |
self.lang = lang |
154 |
self.debug('request language: %s', lang) |
|
5244
5467674ad101
[web] put a fake object that raise Unauthorized on any attribute access as req.cnx and req._user, so we are properly asked to authenticated on any view that tries to do something with one of those attributes (instead of doing defensive programming everywhere we're doing that)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5223
diff
changeset
|
155 |
if self.cnx: |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
156 |
self.cnx.set_session_props(lang=lang) |
1426 | 157 |
|
0 | 158 |
# input form parameters management ######################################## |
1426 | 159 |
|
0 | 160 |
# common form parameters which should be protected against html values |
161 |
# XXX can't add 'eid' for instance since it may be multivalued |
|
162 |
# dont put rql as well, if query contains < and > it will be corrupted! |
|
1426 | 163 |
no_script_form_params = set(('vid', |
164 |
'etype', |
|
0 | 165 |
'vtitle', 'title', |
166 |
'__message', |
|
167 |
'__redirectvid', '__redirectrql')) |
|
1426 | 168 |
|
0 | 169 |
def setup_params(self, params): |
170 |
"""WARNING: we're intentionaly leaving INTERNAL_FIELD_VALUE here |
|
171 |
||
1426 | 172 |
subclasses should overrides to |
0 | 173 |
""" |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
174 |
self.form = {} |
0 | 175 |
if params is None: |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
176 |
return |
0 | 177 |
encoding = self.encoding |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
178 |
for param, val in params.iteritems(): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
179 |
if isinstance(val, (tuple, list)): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
180 |
val = [unicode(x, encoding) for x in val] |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
181 |
if len(val) == 1: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
182 |
val = val[0] |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
183 |
elif isinstance(val, str): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
184 |
val = unicode(val, encoding) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
185 |
if param in self.no_script_form_params and val: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
186 |
val = self.no_script_form_param(param, val) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
187 |
if param == '_cwmsgid': |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
188 |
self.set_message_id(val) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
189 |
elif param == '__message': |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
190 |
self.set_message(val) |
0 | 191 |
else: |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
192 |
self.form[param] = val |
1426 | 193 |
|
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
194 |
def no_script_form_param(self, param, value): |
0 | 195 |
"""ensure there is no script in a user form param |
196 |
||
197 |
by default return a cleaned string instead of raising a security |
|
198 |
exception |
|
199 |
||
200 |
this method should be called on every user input (form at least) fields |
|
201 |
that are at some point inserted in a generated html page to protect |
|
202 |
against script kiddies |
|
203 |
""" |
|
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
204 |
# safety belt for strange urls like http://...?vtitle=yo&vtitle=yo |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
205 |
if isinstance(value, (list, tuple)): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
206 |
self.error('no_script_form_param got a list (%s). Who generated the URL ?', |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
207 |
repr(value)) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
208 |
value = value[0] |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
209 |
return remove_html_tags(value) |
1426 | 210 |
|
0 | 211 |
def list_form_param(self, param, form=None, pop=False): |
212 |
"""get param from form parameters and return its value as a list, |
|
213 |
skipping internal markers if any |
|
1426 | 214 |
|
0 | 215 |
* if the parameter isn't defined, return an empty list |
216 |
* if the parameter is a single (unicode) value, return a list |
|
217 |
containing that value |
|
218 |
* if the parameter is already a list or tuple, just skip internal |
|
219 |
markers |
|
220 |
||
221 |
if pop is True, the parameter is removed from the form dictionnary |
|
222 |
""" |
|
223 |
if form is None: |
|
224 |
form = self.form |
|
1426 | 225 |
return list_form_param(form, param, pop) |
226 |
||
0 | 227 |
|
228 |
def reset_headers(self): |
|
229 |
"""used by AutomaticWebTest to clear html headers between tests on |
|
230 |
the same resultset |
|
231 |
""" |
|
232 |
self.html_headers = HTMLHead() |
|
233 |
return self |
|
234 |
||
235 |
# web state helpers ####################################################### |
|
1426 | 236 |
|
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
237 |
@property |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
238 |
def message(self): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
239 |
try: |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
240 |
return self.session.data.pop(self._msgid, '') |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
241 |
except AttributeError: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
242 |
try: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
243 |
return self._msg |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
244 |
except AttributeError: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
245 |
return None |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
246 |
|
0 | 247 |
def set_message(self, msg): |
248 |
assert isinstance(msg, unicode) |
|
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
249 |
self._msg = msg |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
250 |
|
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
251 |
def set_message_id(self, msgid): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
252 |
self._msgid = msgid |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
253 |
|
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
254 |
@cached |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
255 |
def redirect_message_id(self): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
256 |
return make_uid() |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
257 |
|
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
258 |
def set_redirect_message(self, msg): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
259 |
assert isinstance(msg, unicode) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
260 |
msgid = self.redirect_message_id() |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
261 |
self.session.data[msgid] = msg |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
262 |
return msgid |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
263 |
|
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
264 |
def append_to_redirect_message(self, msg): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
265 |
msgid = self.redirect_message_id() |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
266 |
currentmsg = self.session.data.get(msgid) |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
267 |
if currentmsg is not None: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
268 |
currentmsg = '%s %s' % (currentmsg, msg) |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
269 |
else: |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
270 |
currentmsg = msg |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
271 |
self.session.data[msgid] = currentmsg |
4897
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
272 |
return msgid |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
273 |
|
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
274 |
def reset_message(self): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
275 |
if hasattr(self, '_msg'): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
276 |
del self._msg |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
277 |
if hasattr(self, '_msgid'): |
e402e0b32075
[web] start a new message system based on id of message stored in session's data
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4851
diff
changeset
|
278 |
del self._msgid |
1426 | 279 |
|
0 | 280 |
def update_search_state(self): |
281 |
"""update the current search state""" |
|
282 |
searchstate = self.form.get('__mode') |
|
5244
5467674ad101
[web] put a fake object that raise Unauthorized on any attribute access as req.cnx and req._user, so we are properly asked to authenticated on any view that tries to do something with one of those attributes (instead of doing defensive programming everywhere we're doing that)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5223
diff
changeset
|
283 |
if not searchstate and self.cnx: |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
284 |
searchstate = self.session.data.get('search_state', 'normal') |
0 | 285 |
self.set_search_state(searchstate) |
286 |
||
287 |
def set_search_state(self, searchstate): |
|
288 |
"""set a new search state""" |
|
289 |
if searchstate is None or searchstate == 'normal': |
|
290 |
self.search_state = (searchstate or 'normal',) |
|
291 |
else: |
|
292 |
self.search_state = ('linksearch', searchstate.split(':')) |
|
293 |
assert len(self.search_state[-1]) == 4 |
|
5244
5467674ad101
[web] put a fake object that raise Unauthorized on any attribute access as req.cnx and req._user, so we are properly asked to authenticated on any view that tries to do something with one of those attributes (instead of doing defensive programming everywhere we're doing that)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5223
diff
changeset
|
294 |
if self.cnx: |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
295 |
self.session.data['search_state'] = searchstate |
0 | 296 |
|
1173
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
297 |
def match_search_state(self, rset): |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
298 |
"""when searching an entity to create a relation, return True if entities in |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
299 |
the given rset may be used as relation end |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
300 |
""" |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
301 |
try: |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
302 |
searchedtype = self.search_state[1][-1] |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
303 |
except IndexError: |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
304 |
return False # no searching for association |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
305 |
for etype in rset.column_types(0): |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
306 |
if etype != searchedtype: |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
307 |
return False |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
308 |
return True |
8f123fd081f4
forgot to add that expected method (was a function in view.__init__)
sylvain.thenault@logilab.fr
parents:
1013
diff
changeset
|
309 |
|
0 | 310 |
def update_breadcrumbs(self): |
311 |
"""stores the last visisted page in session data""" |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
312 |
searchstate = self.session.data.get('search_state') |
0 | 313 |
if searchstate == 'normal': |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
314 |
breadcrumbs = self.session.data.get('breadcrumbs') |
0 | 315 |
if breadcrumbs is None: |
316 |
breadcrumbs = SizeConstrainedList(10) |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
317 |
self.session.data['breadcrumbs'] = breadcrumbs |
4864
e77d3e95872d
[web req] check latest url in breadcrumbs is not the same as the current one to keep more valuable information
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
318 |
breadcrumbs.append(self.url()) |
e77d3e95872d
[web req] check latest url in breadcrumbs is not the same as the current one to keep more valuable information
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
319 |
else: |
e77d3e95872d
[web req] check latest url in breadcrumbs is not the same as the current one to keep more valuable information
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
320 |
url = self.url() |
4974
025a491bad0c
take care to empty breadcrumbs
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4868
diff
changeset
|
321 |
if breadcrumbs and breadcrumbs[-1] != url: |
4864
e77d3e95872d
[web req] check latest url in breadcrumbs is not the same as the current one to keep more valuable information
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
322 |
breadcrumbs.append(url) |
0 | 323 |
|
324 |
def last_visited_page(self): |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
325 |
breadcrumbs = self.session.data.get('breadcrumbs') |
0 | 326 |
if breadcrumbs: |
327 |
return breadcrumbs.pop() |
|
328 |
return self.base_url() |
|
329 |
||
2792
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
330 |
def user_rql_callback(self, args, msg=None): |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
331 |
"""register a user callback to execute some rql query and return an url |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
332 |
to call it ready to be inserted in html |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
333 |
""" |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
334 |
def rqlexec(req, rql, args=None, key=None): |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
335 |
req.execute(rql, args, key) |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
336 |
return self.user_callback(rqlexec, args, msg) |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
337 |
|
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
338 |
def user_callback(self, cb, args, msg=None, nonify=False): |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
339 |
"""register the given user callback and return an url to call it ready to be |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
340 |
inserted in html |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
341 |
""" |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
342 |
self.add_js('cubicweb.ajax.js') |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
343 |
cbname = self.register_onetime_callback(cb, *args) |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
344 |
msg = dumps(msg or '') |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
345 |
return "javascript:userCallbackThenReloadPage('%s', %s)" % ( |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
346 |
cbname, msg) |
135580d15d42
rename and move cw.RequestSessionMixIn to cw.req.RequestSessionBase; move some appobjects methods where they actually belong to
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2788
diff
changeset
|
347 |
|
0 | 348 |
def register_onetime_callback(self, func, *args): |
349 |
cbname = 'cb_%s' % ( |
|
350 |
sha.sha('%s%s%s%s' % (time.time(), func.__name__, |
|
1426 | 351 |
random.random(), |
0 | 352 |
self.user.login)).hexdigest()) |
353 |
def _cb(req): |
|
354 |
try: |
|
355 |
ret = func(req, *args) |
|
356 |
except TypeError: |
|
357 |
from warnings import warn |
|
358 |
warn('user callback should now take request as argument') |
|
1426 | 359 |
ret = func(*args) |
0 | 360 |
self.unregister_callback(self.pageid, cbname) |
361 |
return ret |
|
362 |
self.set_page_data(cbname, _cb) |
|
363 |
return cbname |
|
1426 | 364 |
|
0 | 365 |
def unregister_callback(self, pageid, cbname): |
366 |
assert pageid is not None |
|
367 |
assert cbname.startswith('cb_') |
|
368 |
self.info('unregistering callback %s for pageid %s', cbname, pageid) |
|
369 |
self.del_page_data(cbname) |
|
370 |
||
371 |
def clear_user_callbacks(self): |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
372 |
if self.session is not None: # XXX |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
373 |
for key in self.session.data.keys(): |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
374 |
if key.startswith('cb_'): |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
375 |
del self.session.data[key] |
1426 | 376 |
|
0 | 377 |
# web edition helpers ##################################################### |
1426 | 378 |
|
0 | 379 |
@cached # so it's writed only once |
380 |
def fckeditor_config(self): |
|
890
3530baff9120
make fckeditor actually optional, fix its config, avoid needs for a link to fckeditor.js
sylvain.thenault@logilab.fr
parents:
662
diff
changeset
|
381 |
self.add_js('fckeditor/fckeditor.js') |
0 | 382 |
self.html_headers.define_var('fcklang', self.lang) |
383 |
self.html_headers.define_var('fckconfigpath', |
|
890
3530baff9120
make fckeditor actually optional, fix its config, avoid needs for a link to fckeditor.js
sylvain.thenault@logilab.fr
parents:
662
diff
changeset
|
384 |
self.build_url('data/cubicweb.fckcwconfig.js')) |
1013
948a3882c94a
add a use_fckeditor method on http request
sylvain.thenault@logilab.fr
parents:
940
diff
changeset
|
385 |
def use_fckeditor(self): |
948a3882c94a
add a use_fckeditor method on http request
sylvain.thenault@logilab.fr
parents:
940
diff
changeset
|
386 |
return self.vreg.config.fckeditor_installed() and self.property_value('ui.fckeditor') |
0 | 387 |
|
388 |
def edited_eids(self, withtype=False): |
|
389 |
"""return a list of edited eids""" |
|
390 |
yielded = False |
|
391 |
# warning: use .keys since the caller may change `form` |
|
392 |
form = self.form |
|
393 |
try: |
|
394 |
eids = form['eid'] |
|
395 |
except KeyError: |
|
4155
80cc9c6ddcf0
NothingToEdit is not a ValidationError, simplify
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4154
diff
changeset
|
396 |
raise NothingToEdit(self._('no selected entities')) |
0 | 397 |
if isinstance(eids, basestring): |
398 |
eids = (eids,) |
|
399 |
for peid in eids: |
|
400 |
if withtype: |
|
401 |
typekey = '__type:%s' % peid |
|
402 |
assert typekey in form, 'no entity type specified' |
|
403 |
yield peid, form[typekey] |
|
404 |
else: |
|
405 |
yield peid |
|
406 |
yielded = True |
|
407 |
if not yielded: |
|
4155
80cc9c6ddcf0
NothingToEdit is not a ValidationError, simplify
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4154
diff
changeset
|
408 |
raise NothingToEdit(self._('no selected entities')) |
0 | 409 |
|
410 |
# minparams=3 by default: at least eid, __type, and some params to change |
|
411 |
def extract_entity_params(self, eid, minparams=3): |
|
412 |
"""extract form parameters relative to the given eid""" |
|
413 |
params = {} |
|
414 |
eid = str(eid) |
|
415 |
form = self.form |
|
416 |
for param in form: |
|
417 |
try: |
|
418 |
name, peid = param.split(':', 1) |
|
419 |
except ValueError: |
|
420 |
if not param.startswith('__') and param != "eid": |
|
421 |
self.warning('param %s mis-formatted', param) |
|
422 |
continue |
|
423 |
if peid == eid: |
|
424 |
value = form[param] |
|
425 |
if value == INTERNAL_FIELD_VALUE: |
|
426 |
value = None |
|
427 |
params[name] = value |
|
428 |
params['eid'] = eid |
|
429 |
if len(params) < minparams: |
|
430 |
raise RequestError(self._('missing parameters for entity %s') % eid) |
|
431 |
return params |
|
1426 | 432 |
|
4277
35cd057339b2
turn all the stuff used to handle 'generic relations' in forms into proper
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4252
diff
changeset
|
433 |
# XXX this should go to the GenericRelationsField. missing edition cancel protocol. |
0 | 434 |
|
435 |
def remove_pending_operations(self): |
|
436 |
"""shortcut to clear req's pending_{delete,insert} entries |
|
437 |
||
438 |
This is needed when the edition is completed (whether it's validated |
|
439 |
or cancelled) |
|
440 |
""" |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
441 |
self.session.data.pop('pending_insert', None) |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
442 |
self.session.data.pop('pending_delete', None) |
0 | 443 |
|
444 |
def cancel_edition(self, errorurl): |
|
445 |
"""remove pending operations and `errorurl`'s specific stored data |
|
446 |
""" |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
447 |
self.session.data.pop(errorurl, None) |
0 | 448 |
self.remove_pending_operations() |
1426 | 449 |
|
0 | 450 |
# high level methods for HTTP headers management ########################## |
451 |
||
452 |
# must be cached since login/password are popped from the form dictionary |
|
453 |
# and this method may be called multiple times during authentication |
|
454 |
@cached |
|
455 |
def get_authorization(self): |
|
456 |
"""Parse and return the Authorization header""" |
|
457 |
if self.authmode == "cookie": |
|
458 |
try: |
|
459 |
user = self.form.pop("__login") |
|
460 |
passwd = self.form.pop("__password", '') |
|
461 |
return user, passwd.encode('UTF8') |
|
462 |
except KeyError: |
|
463 |
self.debug('no login/password in form params') |
|
464 |
return None, None |
|
465 |
else: |
|
466 |
return self.header_authorization() |
|
1426 | 467 |
|
0 | 468 |
def get_cookie(self): |
469 |
"""retrieve request cookies, returns an empty cookie if not found""" |
|
470 |
try: |
|
471 |
return Cookie.SimpleCookie(self.get_header('Cookie')) |
|
472 |
except KeyError: |
|
473 |
return Cookie.SimpleCookie() |
|
474 |
||
3653
ef71abb1e77b
[req] new expires argument to set_cookie
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3293
diff
changeset
|
475 |
def set_cookie(self, cookie, key, maxage=300, expires=None): |
0 | 476 |
"""set / update a cookie key |
477 |
||
478 |
by default, cookie will be available for the next 5 minutes. |
|
479 |
Give maxage = None to have a "session" cookie expiring when the |
|
480 |
client close its browser |
|
481 |
""" |
|
482 |
morsel = cookie[key] |
|
483 |
if maxage is not None: |
|
484 |
morsel['Max-Age'] = maxage |
|
3653
ef71abb1e77b
[req] new expires argument to set_cookie
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3293
diff
changeset
|
485 |
if expires: |
ef71abb1e77b
[req] new expires argument to set_cookie
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3293
diff
changeset
|
486 |
morsel['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S %z') |
0 | 487 |
# make sure cookie is set on the correct path |
488 |
morsel['path'] = self.base_url_path() |
|
489 |
self.add_header('Set-Cookie', morsel.OutputString()) |
|
490 |
||
491 |
def remove_cookie(self, cookie, key): |
|
492 |
"""remove a cookie by expiring it""" |
|
3653
ef71abb1e77b
[req] new expires argument to set_cookie
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3293
diff
changeset
|
493 |
self.set_cookie(cookie, key, maxage=0, expires=date(1970, 1, 1)) |
0 | 494 |
|
495 |
def set_content_type(self, content_type, filename=None, encoding=None): |
|
496 |
"""set output content type for this request. An optional filename |
|
497 |
may be given |
|
498 |
""" |
|
499 |
if content_type.startswith('text/'): |
|
500 |
content_type += ';charset=' + (encoding or self.encoding) |
|
501 |
self.set_header('content-type', content_type) |
|
502 |
if filename: |
|
503 |
if isinstance(filename, unicode): |
|
504 |
filename = header(filename).encode() |
|
505 |
self.set_header('content-disposition', 'inline; filename=%s' |
|
506 |
% filename) |
|
507 |
||
508 |
# high level methods for HTML headers management ########################## |
|
509 |
||
2258
79bc598c6411
when request is a json request, bind on 'ajax-loaded' instead of document.ready()
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2255
diff
changeset
|
510 |
def add_onload(self, jscode): |
4851
e55bdd10421e
remove deprecation warning introduced by add_onload api change
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4719
diff
changeset
|
511 |
self.html_headers.add_onload(jscode) |
2258
79bc598c6411
when request is a json request, bind on 'ajax-loaded' instead of document.ready()
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2255
diff
changeset
|
512 |
|
0 | 513 |
def add_js(self, jsfiles, localfile=True): |
514 |
"""specify a list of JS files to include in the HTML headers |
|
515 |
:param jsfiles: a JS filename or a list of JS filenames |
|
516 |
:param localfile: if True, the default data dir prefix is added to the |
|
517 |
JS filename |
|
518 |
""" |
|
519 |
if isinstance(jsfiles, basestring): |
|
520 |
jsfiles = (jsfiles,) |
|
521 |
for jsfile in jsfiles: |
|
522 |
if localfile: |
|
523 |
jsfile = self.datadir_url + jsfile |
|
524 |
self.html_headers.add_js(jsfile) |
|
525 |
||
4860
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
526 |
def add_css(self, cssfiles, media=u'all', localfile=True, ieonly=False, |
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
527 |
iespec=u'[if lt IE 8]'): |
0 | 528 |
"""specify a CSS file to include in the HTML headers |
529 |
:param cssfiles: a CSS filename or a list of CSS filenames |
|
530 |
:param media: the CSS's media if necessary |
|
531 |
:param localfile: if True, the default data dir prefix is added to the |
|
532 |
CSS filename |
|
4860
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
533 |
:param ieonly: True if this css is specific to IE |
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
534 |
:param iespec: conditional expression that will be used around |
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
535 |
the css inclusion. cf: |
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
536 |
http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx |
0 | 537 |
""" |
538 |
if isinstance(cssfiles, basestring): |
|
539 |
cssfiles = (cssfiles,) |
|
540 |
if ieonly: |
|
541 |
if self.ie_browser(): |
|
4860
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
542 |
extraargs = [iespec] |
0 | 543 |
add_css = self.html_headers.add_ie_css |
544 |
else: |
|
545 |
return # no need to do anything on non IE browsers |
|
546 |
else: |
|
4860
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
547 |
extraargs = [] |
0 | 548 |
add_css = self.html_headers.add_css |
549 |
for cssfile in cssfiles: |
|
550 |
if localfile: |
|
551 |
cssfile = self.datadir_url + cssfile |
|
4860
cedb6afdb7da
[web] fix #736332: iespec functionality for add_css
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
4719
diff
changeset
|
552 |
add_css(cssfile, media, *extraargs) |
1426 | 553 |
|
1801
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
554 |
def build_ajax_replace_url(self, nodeid, rql, vid, replacemode='replace', |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
555 |
**extraparams): |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
556 |
"""builds an ajax url that will replace `nodeid`s content |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
557 |
:param nodeid: the dom id of the node to replace |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
558 |
:param rql: rql to execute |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
559 |
:param vid: the view to apply on the resultset |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
560 |
:param replacemode: defines how the replacement should be done. |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
561 |
Possible values are : |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
562 |
- 'replace' to replace the node's content with the generated HTML |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
563 |
- 'swap' to replace the node itself with the generated HTML |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
564 |
- 'append' to append the generated HTML to the node's content |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
565 |
""" |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
566 |
url = self.build_url('view', rql=rql, vid=vid, __notemplate=1, |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
567 |
**extraparams) |
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
568 |
return "javascript: loadxhtml('%s', '%s', '%s')" % ( |
2312
af4d8f75c5db
use xml_escape
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2258
diff
changeset
|
569 |
nodeid, xml_escape(url), replacemode) |
1801
672acc730ce5
ajax_replace_url becomes obsolete, req.build_ajax_replace_url should be used instead
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1718
diff
changeset
|
570 |
|
0 | 571 |
# urls/path management #################################################### |
1426 | 572 |
|
0 | 573 |
def url(self, includeparams=True): |
574 |
"""return currently accessed url""" |
|
575 |
return self.base_url() + self.relative_path(includeparams) |
|
576 |
||
577 |
def _datadir_url(self): |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2315
diff
changeset
|
578 |
"""return url of the instance's data directory""" |
0 | 579 |
return self.base_url() + 'data%s/' % self.vreg.config.instance_md5_version() |
1426 | 580 |
|
0 | 581 |
def selected(self, url): |
582 |
"""return True if the url is equivalent to currently accessed url""" |
|
583 |
reqpath = self.relative_path().lower() |
|
584 |
baselen = len(self.base_url()) |
|
585 |
return (reqpath == url[baselen:].lower()) |
|
586 |
||
587 |
def base_url_prepend_host(self, hostname): |
|
588 |
protocol, roothost = urlsplit(self.base_url())[:2] |
|
589 |
if roothost.startswith('www.'): |
|
590 |
roothost = roothost[4:] |
|
591 |
return '%s://%s.%s' % (protocol, hostname, roothost) |
|
592 |
||
593 |
def base_url_path(self): |
|
594 |
"""returns the absolute path of the base url""" |
|
595 |
return urlsplit(self.base_url())[2] |
|
1426 | 596 |
|
0 | 597 |
@cached |
598 |
def from_controller(self): |
|
599 |
"""return the id (string) of the controller issuing the request""" |
|
600 |
controller = self.relative_path(False).split('/', 1)[0] |
|
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
601 |
registered_controllers = self.vreg['controllers'].keys() |
0 | 602 |
if controller in registered_controllers: |
603 |
return controller |
|
604 |
return 'view' |
|
1426 | 605 |
|
0 | 606 |
def external_resource(self, rid, default=_MARKER): |
607 |
"""return a path to an external resource, using its identifier |
|
608 |
||
609 |
raise KeyError if the resource is not defined |
|
610 |
""" |
|
611 |
try: |
|
612 |
value = self.vreg.config.ext_resources[rid] |
|
613 |
except KeyError: |
|
614 |
if default is _MARKER: |
|
615 |
raise |
|
616 |
return default |
|
617 |
if value is None: |
|
618 |
return None |
|
619 |
baseurl = self.datadir_url[:-1] # remove trailing / |
|
620 |
if isinstance(value, list): |
|
621 |
return [v.replace('DATADIR', baseurl) for v in value] |
|
622 |
return value.replace('DATADIR', baseurl) |
|
623 |
external_resource = cached(external_resource, keyarg=1) |
|
624 |
||
625 |
def validate_cache(self): |
|
626 |
"""raise a `DirectResponse` exception if a cached page along the way |
|
627 |
exists and is still usable. |
|
628 |
||
629 |
calls the client-dependant implementation of `_validate_cache` |
|
630 |
""" |
|
631 |
self._validate_cache() |
|
632 |
if self.http_method() == 'HEAD': |
|
633 |
raise StatusResponse(200, '') |
|
1426 | 634 |
|
0 | 635 |
# abstract methods to override according to the web front-end ############# |
1426 | 636 |
|
0 | 637 |
def http_method(self): |
638 |
"""returns 'POST', 'GET', 'HEAD', etc.""" |
|
639 |
raise NotImplementedError() |
|
640 |
||
641 |
def _validate_cache(self): |
|
642 |
"""raise a `DirectResponse` exception if a cached page along the way |
|
643 |
exists and is still usable |
|
644 |
""" |
|
645 |
raise NotImplementedError() |
|
1426 | 646 |
|
0 | 647 |
def relative_path(self, includeparams=True): |
648 |
"""return the normalized path of the request (ie at least relative |
|
2476
1294a6bdf3bf
application -> instance where it makes sense
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2315
diff
changeset
|
649 |
to the instance's root, but some other normalization may be needed |
0 | 650 |
so that the returned path may be used to compare to generated urls |
651 |
||
652 |
:param includeparams: |
|
653 |
boolean indicating if GET form parameters should be kept in the path |
|
654 |
""" |
|
655 |
raise NotImplementedError() |
|
656 |
||
657 |
def get_header(self, header, default=None): |
|
658 |
"""return the value associated with the given input HTTP header, |
|
659 |
raise KeyError if the header is not set |
|
660 |
""" |
|
661 |
raise NotImplementedError() |
|
662 |
||
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
663 |
def set_header(self, header, value, raw=True): |
0 | 664 |
"""set an output HTTP header""" |
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
665 |
if raw: |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
666 |
# adding encoded header is important, else page content |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
667 |
# will be reconverted back to unicode and apart unefficiency, this |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
668 |
# may cause decoding problem (e.g. when downloading a file) |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
669 |
self.headers_out.setRawHeaders(header, [str(value)]) |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
670 |
else: |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
671 |
self.headers_out.setHeader(header, value) |
0 | 672 |
|
673 |
def add_header(self, header, value): |
|
674 |
"""add an output HTTP header""" |
|
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
675 |
# adding encoded header is important, else page content |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
676 |
# will be reconverted back to unicode and apart unefficiency, this |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
677 |
# may cause decoding problem (e.g. when downloading a file) |
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
678 |
self.headers_out.addRawHeader(header, str(value)) |
1426 | 679 |
|
0 | 680 |
def remove_header(self, header): |
681 |
"""remove an output HTTP header""" |
|
5155
1dea6e0fdfc1
Switched from TwistedWeb2 to TwistedWeb
Adrien Chauve <adrien.chauve@logilab.fr>
parents:
4984
diff
changeset
|
682 |
self.headers_out.removeHeader(header) |
1426 | 683 |
|
0 | 684 |
def header_authorization(self): |
685 |
"""returns a couple (auth-type, auth-value)""" |
|
686 |
auth = self.get_header("Authorization", None) |
|
687 |
if auth: |
|
688 |
scheme, rest = auth.split(' ', 1) |
|
689 |
scheme = scheme.lower() |
|
690 |
try: |
|
691 |
assert scheme == "basic" |
|
692 |
user, passwd = base64.decodestring(rest).split(":", 1) |
|
693 |
# XXX HTTP header encoding: use email.Header? |
|
694 |
return user.decode('UTF8'), passwd |
|
695 |
except Exception, ex: |
|
696 |
self.debug('bad authorization %s (%s: %s)', |
|
697 |
auth, ex.__class__.__name__, ex) |
|
698 |
return None, None |
|
699 |
||
2788
8d3dbe577d3a
R put version info in deprecation warnings
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2650
diff
changeset
|
700 |
@deprecated("[3.4] use parse_accept_header('Accept-Language')") |
0 | 701 |
def header_accept_language(self): |
702 |
"""returns an ordered list of preferred languages""" |
|
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
703 |
return [value.split('-')[0] for value in |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
704 |
self.parse_accept_header('Accept-Language')] |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
705 |
|
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
706 |
def parse_accept_header(self, header): |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
707 |
"""returns an ordered list of preferred languages""" |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
708 |
accepteds = self.get_header(header, '') |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
709 |
values = [] |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
710 |
for info in accepteds.split(','): |
0 | 711 |
try: |
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
712 |
value, scores = info.split(';', 1) |
0 | 713 |
except ValueError: |
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
714 |
value = info |
0 | 715 |
score = 1.0 |
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
716 |
else: |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
717 |
for score in scores.split(';'): |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
718 |
try: |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
719 |
scorekey, scoreval = score.split('=') |
1717
d2c4d3bd0602
correct wrong condition and missing import
Graziella Toutoungis <graziella.toutoungis@logilab.fr>
parents:
1716
diff
changeset
|
720 |
if scorekey == 'q': # XXX 'level' |
4719
aaed3f813ef8
kill dead/useless code as suggested by pylint
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
4529
diff
changeset
|
721 |
score = float(scoreval) |
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
722 |
except ValueError: |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
723 |
continue |
1718
26ff2d292183
correct the values list append
Graziella Toutoungis <graziella.toutoungis@logilab.fr>
parents:
1717
diff
changeset
|
724 |
values.append((score, value)) |
1716
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
725 |
values.sort(reverse=True) |
b12d9e22bac3
basic support for http Accept header (untested)
sylvain.thenault@logilab.fr
parents:
1560
diff
changeset
|
726 |
return (value for (score, value) in values) |
0 | 727 |
|
728 |
def header_if_modified_since(self): |
|
729 |
"""If the HTTP header If-modified-since is set, return the equivalent |
|
730 |
mx date time value (GMT), else return None |
|
731 |
""" |
|
732 |
raise NotImplementedError() |
|
1426 | 733 |
|
3094
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
734 |
def demote_to_html(self): |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
735 |
"""helper method to dynamically set request content type to text/html |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
736 |
|
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
737 |
The global doctype and xmldec must also be changed otherwise the browser |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
738 |
will display '<[' at the beginning of the page |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
739 |
""" |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
740 |
self.set_content_type('text/html') |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
741 |
self.main_stream.doctype = TRANSITIONAL_DOCTYPE_NOEXT |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
742 |
self.main_stream.xmldecl = u'' |
978ed8c2c0e4
[googlemap] #344872 set request content-type to text/html
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
2650
diff
changeset
|
743 |
|
0 | 744 |
# page data management #################################################### |
745 |
||
746 |
def get_page_data(self, key, default=None): |
|
747 |
"""return value associated to `key` in curernt page data""" |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
748 |
page_data = self.session.data.get(self.pageid) |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
749 |
if page_data is None: |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
750 |
return default |
0 | 751 |
return page_data.get(key, default) |
1426 | 752 |
|
0 | 753 |
def set_page_data(self, key, value): |
754 |
"""set value associated to `key` in current page data""" |
|
755 |
self.html_headers.add_unload_pagedata() |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
756 |
page_data = self.session.data.setdefault(self.pageid, {}) |
0 | 757 |
page_data[key] = value |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
758 |
self.session.data[self.pageid] = page_data |
1426 | 759 |
|
0 | 760 |
def del_page_data(self, key=None): |
761 |
"""remove value associated to `key` in current page data |
|
762 |
if `key` is None, all page data will be cleared |
|
763 |
""" |
|
764 |
if key is None: |
|
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
765 |
self.session.data.pop(self.pageid, None) |
0 | 766 |
else: |
5223
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
767 |
try: |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
768 |
del self.session.data[self.pageid][key] |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
769 |
except KeyError: |
6abd6e3599f4
#773448: refactor session and 'no connection' handling, by introducing proper web session. We should now be able to see page even when no anon is configured, and be redirected to the login form as soon as one tries to do a query.
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
5155
diff
changeset
|
770 |
pass |
0 | 771 |
|
772 |
# user-agent detection #################################################### |
|
773 |
||
774 |
@cached |
|
775 |
def useragent(self): |
|
776 |
return self.get_header('User-Agent', None) |
|
777 |
||
778 |
def ie_browser(self): |
|
779 |
useragent = self.useragent() |
|
780 |
return useragent and 'MSIE' in useragent |
|
1426 | 781 |
|
0 | 782 |
def xhtml_browser(self): |
2558
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
783 |
"""return True if the browser is considered as xhtml compatible. |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
784 |
|
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
785 |
If the instance is configured to always return text/html and not |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
786 |
application/xhtml+xml, this method will always return False, even though |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
787 |
this is semantically different |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
788 |
""" |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
789 |
if self.vreg.config['force-html-content-type']: |
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
790 |
return False |
0 | 791 |
useragent = self.useragent() |
1421
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
792 |
# * MSIE/Konqueror does not support xml content-type |
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
793 |
# * Opera supports xhtml and handles namespaces properly but it breaks |
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
794 |
# jQuery.attr() |
495
f8b1edfe9621
[#80966] Opera supports xhtml and handles namespaces properly but it breaks jQuery.attr(), so xhtml_browser return False if the webbrowser is opera
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents:
0
diff
changeset
|
795 |
if useragent and ('MSIE' in useragent or 'KHTML' in useragent |
f8b1edfe9621
[#80966] Opera supports xhtml and handles namespaces properly but it breaks jQuery.attr(), so xhtml_browser return False if the webbrowser is opera
Stephanie Marcu <stephanie.marcu@logilab.fr>
parents:
0
diff
changeset
|
796 |
or 'Opera' in useragent): |
0 | 797 |
return False |
798 |
return True |
|
799 |
||
1421
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
800 |
def html_content_type(self): |
2558
81c8b5312f9c
move test on force-html-content-type to xhtml_browser method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2556
diff
changeset
|
801 |
if self.xhtml_browser(): |
1421
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
802 |
return 'application/xhtml+xml' |
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
803 |
return 'text/html' |
77ee26df178f
doc type handling refactoring: do the ext substitution at the module level
sylvain.thenault@logilab.fr
parents:
1173
diff
changeset
|
804 |
|
2559
46859078c866
[R xhtml] remove xhtml_wrap* function, use instead a single req.document_surrounding_div method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2558
diff
changeset
|
805 |
def document_surrounding_div(self): |
46859078c866
[R xhtml] remove xhtml_wrap* function, use instead a single req.document_surrounding_div method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2558
diff
changeset
|
806 |
if self.xhtml_browser(): |
4454
aba1b563705b
[request] add a note about the encoding mgmt (or lack thereof)
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
4212
diff
changeset
|
807 |
return (u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE + # XXX encoding ? |
2559
46859078c866
[R xhtml] remove xhtml_wrap* function, use instead a single req.document_surrounding_div method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2558
diff
changeset
|
808 |
u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">') |
46859078c866
[R xhtml] remove xhtml_wrap* function, use instead a single req.document_surrounding_div method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2558
diff
changeset
|
809 |
return u'<div>' |
46859078c866
[R xhtml] remove xhtml_wrap* function, use instead a single req.document_surrounding_div method
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2558
diff
changeset
|
810 |
|
0 | 811 |
from cubicweb import set_log_methods |
812 |
set_log_methods(CubicWebRequestBase, LOGGER) |