Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / util.py @ 9a94c0f1

History | View | Annotate | Download (11.1 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 5ce3ce4f Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 5ce3ce4f Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 5ce3ce4f Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 5ce3ce4f Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5ce3ce4f Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 0905ccd2 Sofia Papagiannaki
import logging
35 18ffbee1 Sofia Papagiannaki
import time
36 6936103e Kostas Papadimitriou
import urllib
37 63ecdd20 Sofia Papagiannaki
38 c0b26605 Sofia Papagiannaki
from urlparse import urlparse
39 64cd4730 Antony Chazapis
from datetime import tzinfo, timedelta
40 c0b26605 Sofia Papagiannaki
41 51db2da2 Giorgos Korfiatis
from django.http import HttpResponse, HttpResponseBadRequest, urlencode
42 0905ccd2 Sofia Papagiannaki
from django.template import RequestContext
43 111f3da6 Sofia Papagiannaki
from django.contrib.auth import authenticate
44 63ecdd20 Sofia Papagiannaki
from django.core.urlresolvers import reverse
45 440f7c0c Kostas Papadimitriou
from django.shortcuts import redirect
46 9a06d96f Olga Brani
from django.core.exceptions import ValidationError, ObjectDoesNotExist
47 ae497612 Olga Brani
from django.utils.translation import ugettext as _
48 ae497612 Olga Brani
49 aab4d540 Sofia Papagiannaki
from astakos.im.models import AstakosUser, Invitation
50 111f3da6 Sofia Papagiannaki
from astakos.im.functions import login
51 0a7a4104 Kostas Papadimitriou
from astakos.im import settings
52 64cd4730 Antony Chazapis
53 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
54 ae497612 Olga Brani
55 e015e9e6 Sofia Papagiannaki
logger = logging.getLogger(__name__)
56 e015e9e6 Sofia Papagiannaki
57 5ce3ce4f Sofia Papagiannaki
58 64cd4730 Antony Chazapis
class UTC(tzinfo):
59 aab4d540 Sofia Papagiannaki
    def utcoffset(self, dt):
60 aab4d540 Sofia Papagiannaki
        return timedelta(0)
61 5ce3ce4f Sofia Papagiannaki
62 aab4d540 Sofia Papagiannaki
    def tzname(self, dt):
63 aab4d540 Sofia Papagiannaki
        return 'UTC'
64 5ce3ce4f Sofia Papagiannaki
65 aab4d540 Sofia Papagiannaki
    def dst(self, dt):
66 aab4d540 Sofia Papagiannaki
        return timedelta(0)
67 64cd4730 Antony Chazapis
68 5ce3ce4f Sofia Papagiannaki
69 64cd4730 Antony Chazapis
def isoformat(d):
70 aab4d540 Sofia Papagiannaki
    """Return an ISO8601 date string that includes a timezone."""
71 5ce3ce4f Sofia Papagiannaki
72 aab4d540 Sofia Papagiannaki
    return d.replace(tzinfo=UTC()).isoformat()
73 0905ccd2 Sofia Papagiannaki
74 5ce3ce4f Sofia Papagiannaki
75 8fb8d0cf Giorgos Korfiatis
def epoch(dt):
76 8fb8d0cf Giorgos Korfiatis
    return int(time.mktime(dt.timetuple()) * 1000)
77 5ce3ce4f Sofia Papagiannaki
78 18ffbee1 Sofia Papagiannaki
79 aab4d540 Sofia Papagiannaki
def get_context(request, extra_context=None, **kwargs):
80 aab4d540 Sofia Papagiannaki
    extra_context = extra_context or {}
81 0905ccd2 Sofia Papagiannaki
    extra_context.update(kwargs)
82 0905ccd2 Sofia Papagiannaki
    return RequestContext(request, extra_context)
83 e2125441 Sofia Papagiannaki
84 5ce3ce4f Sofia Papagiannaki
85 15efc749 Sofia Papagiannaki
def get_invitation(request):
86 15efc749 Sofia Papagiannaki
    """
87 15efc749 Sofia Papagiannaki
    Returns the invitation identified by the ``code``.
88 5ce3ce4f Sofia Papagiannaki

89 0a569195 Sofia Papagiannaki
    Raises ValueError if the invitation is consumed or there is another account
90 0a569195 Sofia Papagiannaki
    associated with this email.
91 15efc749 Sofia Papagiannaki
    """
92 15efc749 Sofia Papagiannaki
    code = request.GET.get('code')
93 15efc749 Sofia Papagiannaki
    if request.method == 'POST':
94 15efc749 Sofia Papagiannaki
        code = request.POST.get('code')
95 15efc749 Sofia Papagiannaki
    if not code:
96 15efc749 Sofia Papagiannaki
        return
97 5ce3ce4f Sofia Papagiannaki
    invitation = Invitation.objects.get(code=code)
98 15efc749 Sofia Papagiannaki
    if invitation.is_consumed:
99 ae497612 Olga Brani
        raise ValueError(_(astakos_messages.INVITATION_CONSUMED_ERR))
100 0a569195 Sofia Papagiannaki
    if reserved_email(invitation.username):
101 ae497612 Olga Brani
        email = invitation.username
102 c0b26605 Sofia Papagiannaki
        raise ValueError(_(astakos_messages.EMAIL_RESERVED) % locals())
103 63ecdd20 Sofia Papagiannaki
    return invitation
104 63ecdd20 Sofia Papagiannaki
105 7c3549f0 Kostas Papadimitriou
106 217994f8 Sofia Papagiannaki
def restrict_next(url, domain=None, allowed_schemes=()):
107 217994f8 Sofia Papagiannaki
    """
108 7c3549f0 Kostas Papadimitriou
    Utility method to validate that provided url is safe to be used as the
109 7c3549f0 Kostas Papadimitriou
    redirect location of an http redirect response. The method parses the
110 7c3549f0 Kostas Papadimitriou
    provided url and identifies if it conforms CORS against provided domain
111 7c3549f0 Kostas Papadimitriou
    AND url scheme matches any of the schemes in `allowed_schemes` parameter.
112 5b65fb47 Kostas Papadimitriou
    If verirication succeeds sanitized safe url is returned. Consider using
113 5b65fb47 Kostas Papadimitriou
    the method's result in the response location header and not the originally
114 5b65fb47 Kostas Papadimitriou
    provided url. If verification fails the method returns None.
115 e5966bd9 Kostas Papadimitriou

116 217994f8 Sofia Papagiannaki
    >>> print restrict_next('/im/feedback', '.okeanos.grnet.gr')
117 217994f8 Sofia Papagiannaki
    /im/feedback
118 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos.okeanos.grnet.gr/im/feedback',
119 7c3549f0 Kostas Papadimitriou
    ...                     '.okeanos.grnet.gr')
120 55baa300 Sofia Papagiannaki
    //pithos.okeanos.grnet.gr/im/feedback
121 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('https://pithos.okeanos.grnet.gr/im/feedback',
122 7c3549f0 Kostas Papadimitriou
    ...                     '.okeanos.grnet.gr')
123 217994f8 Sofia Papagiannaki
    https://pithos.okeanos.grnet.gr/im/feedback
124 1e960db7 Sofia Papagiannaki
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr')
125 217994f8 Sofia Papagiannaki
    None
126 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://127.0.0.1', '.okeanos.grnet.gr',
127 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos'))
128 7c3549f0 Kostas Papadimitriou
    None
129 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://127.0.0.1', '127.0.0.1',
130 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos'))
131 7c3549f0 Kostas Papadimitriou
    pithos://127.0.0.1
132 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com', '.okeanos.grnet.gr')
133 217994f8 Sofia Papagiannaki
    None
134 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com', '.okeanos.grnet.gr')
135 217994f8 Sofia Papagiannaki
    None
136 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com', '.okeanos.grnet.gr')
137 217994f8 Sofia Papagiannaki
    None
138 217994f8 Sofia Papagiannaki
    >>> print restrict_next('https://node1.example.com')
139 217994f8 Sofia Papagiannaki
    https://node1.example.com
140 217994f8 Sofia Papagiannaki
    >>> print restrict_next('//node1.example.com')
141 217994f8 Sofia Papagiannaki
    //node1.example.com
142 217994f8 Sofia Papagiannaki
    >>> print restrict_next('node1.example.com')
143 55baa300 Sofia Papagiannaki
    //node1.example.com
144 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('node1.example.com', allowed_schemes=('pithos',))
145 7c3549f0 Kostas Papadimitriou
    None
146 7c3549f0 Kostas Papadimitriou
    >>> print restrict_next('pithos://localhost', 'localhost',
147 7c3549f0 Kostas Papadimitriou
    ...                     allowed_schemes=('pithos',))
148 7c3549f0 Kostas Papadimitriou
    pithos://localhost
149 217994f8 Sofia Papagiannaki
    """
150 217994f8 Sofia Papagiannaki
    if not url:
151 7c3549f0 Kostas Papadimitriou
        return None
152 7c3549f0 Kostas Papadimitriou
153 217994f8 Sofia Papagiannaki
    parts = urlparse(url, scheme='http')
154 55baa300 Sofia Papagiannaki
    if not parts.netloc and not parts.path.startswith('/'):
155 217994f8 Sofia Papagiannaki
        # fix url if does not conforms RFC 1808
156 217994f8 Sofia Papagiannaki
        url = '//%s' % url
157 217994f8 Sofia Papagiannaki
        parts = urlparse(url, scheme='http')
158 7c3549f0 Kostas Papadimitriou
159 7c3549f0 Kostas Papadimitriou
    if not domain and not allowed_schemes:
160 217994f8 Sofia Papagiannaki
        return url
161 5ce3ce4f Sofia Papagiannaki
162 5b65fb47 Kostas Papadimitriou
    # domain validation
163 7c3549f0 Kostas Papadimitriou
    if domain:
164 7c3549f0 Kostas Papadimitriou
        if not parts.netloc:
165 7c3549f0 Kostas Papadimitriou
            return url
166 7c3549f0 Kostas Papadimitriou
        if parts.netloc.endswith(domain):
167 7c3549f0 Kostas Papadimitriou
            return url
168 7c3549f0 Kostas Papadimitriou
        else:
169 7c3549f0 Kostas Papadimitriou
            return None
170 7c3549f0 Kostas Papadimitriou
171 5b65fb47 Kostas Papadimitriou
    # scheme validation
172 7c3549f0 Kostas Papadimitriou
    if allowed_schemes:
173 7c3549f0 Kostas Papadimitriou
        if parts.scheme in allowed_schemes:
174 7c3549f0 Kostas Papadimitriou
            return url
175 7c3549f0 Kostas Papadimitriou
176 7c3549f0 Kostas Papadimitriou
    return None
177 7c3549f0 Kostas Papadimitriou
178 7c3549f0 Kostas Papadimitriou
179 9efd0075 Kostas Papadimitriou
def restrict_reverse(*args, **kwargs):
180 9efd0075 Kostas Papadimitriou
    """
181 9efd0075 Kostas Papadimitriou
    Like reverse, with an additional restrict_next call to the reverse result.
182 9efd0075 Kostas Papadimitriou
    """
183 9efd0075 Kostas Papadimitriou
    domain = kwargs.pop('restrict_domain', settings.COOKIE_DOMAIN)
184 9efd0075 Kostas Papadimitriou
    url = reverse(*args, **kwargs)
185 9efd0075 Kostas Papadimitriou
    return restrict_next(url, domain=domain)
186 9efd0075 Kostas Papadimitriou
187 9efd0075 Kostas Papadimitriou
188 8fbf5367 root
def prepare_response(request, user, next='', renew=False):
189 63ecdd20 Sofia Papagiannaki
    """Return the unique username and the token
190 63ecdd20 Sofia Papagiannaki
       as 'X-Auth-User' and 'X-Auth-Token' headers,
191 63ecdd20 Sofia Papagiannaki
       or redirect to the URL provided in 'next'
192 63ecdd20 Sofia Papagiannaki
       with the 'user' and 'token' as parameters.
193 5ce3ce4f Sofia Papagiannaki

194 63ecdd20 Sofia Papagiannaki
       Reissue the token even if it has not yet
195 63ecdd20 Sofia Papagiannaki
       expired, if the 'renew' parameter is present
196 63ecdd20 Sofia Papagiannaki
       or user has not a valid token.
197 63ecdd20 Sofia Papagiannaki
    """
198 63ecdd20 Sofia Papagiannaki
    renew = renew or (not user.auth_token)
199 67920ea0 Giorgos Korfiatis
    renew = renew or user.token_expired()
200 63ecdd20 Sofia Papagiannaki
    if renew:
201 bf0c6de5 Sofia Papagiannaki
        user.renew_token(
202 bf0c6de5 Sofia Papagiannaki
            flush_sessions=True,
203 bf0c6de5 Sofia Papagiannaki
            current_key=request.session.session_key
204 bf0c6de5 Sofia Papagiannaki
        )
205 27e26a41 Sofia Papagiannaki
        try:
206 27e26a41 Sofia Papagiannaki
            user.save()
207 27e26a41 Sofia Papagiannaki
        except ValidationError, e:
208 e5966bd9 Kostas Papadimitriou
            return HttpResponseBadRequest(e)
209 e5966bd9 Kostas Papadimitriou
210 0a7a4104 Kostas Papadimitriou
    next = restrict_next(next, domain=settings.COOKIE_DOMAIN)
211 e5966bd9 Kostas Papadimitriou
212 0a7a4104 Kostas Papadimitriou
    if settings.FORCE_PROFILE_UPDATE and \
213 0a7a4104 Kostas Papadimitriou
            not user.is_verified and not user.is_superuser:
214 63ecdd20 Sofia Papagiannaki
        params = ''
215 63ecdd20 Sofia Papagiannaki
        if next:
216 63ecdd20 Sofia Papagiannaki
            params = '?' + urlencode({'next': next})
217 6ff7a7ca Sofia Papagiannaki
        next = reverse('edit_profile') + params
218 5ce3ce4f Sofia Papagiannaki
219 63ecdd20 Sofia Papagiannaki
    response = HttpResponse()
220 5ce3ce4f Sofia Papagiannaki
221 8fbf5367 root
    # authenticate before login
222 8fbf5367 root
    user = authenticate(email=user.email, auth_token=user.auth_token)
223 8fbf5367 root
    login(request, user)
224 b42b0987 Sofia Papagiannaki
    request.session.set_expiry(user.auth_token_expires)
225 5ce3ce4f Sofia Papagiannaki
226 63ecdd20 Sofia Papagiannaki
    if not next:
227 0a7a4104 Kostas Papadimitriou
        next = settings.LOGIN_SUCCESS_URL
228 e5966bd9 Kostas Papadimitriou
229 63ecdd20 Sofia Papagiannaki
    response['Location'] = next
230 63ecdd20 Sofia Papagiannaki
    response.status_code = 302
231 8fbf5367 root
    return response
232 c301698f Sofia Papagiannaki
233 8fb8d0cf Giorgos Korfiatis
234 0a569195 Sofia Papagiannaki
def reserved_email(email):
235 e5966bd9 Kostas Papadimitriou
    return AstakosUser.objects.user_exists(email)
236 5ce3ce4f Sofia Papagiannaki
237 0a569195 Sofia Papagiannaki
238 43332a76 Kostas Papadimitriou
def reserved_verified_email(email):
239 43332a76 Kostas Papadimitriou
    return AstakosUser.objects.verified_user_exists(email)
240 43332a76 Kostas Papadimitriou
241 43332a76 Kostas Papadimitriou
242 0a569195 Sofia Papagiannaki
def get_query(request):
243 f627a979 Sofia Papagiannaki
    try:
244 f627a979 Sofia Papagiannaki
        return request.__getattribute__(request.method)
245 f627a979 Sofia Papagiannaki
    except AttributeError:
246 9a06d96f Olga Brani
        return {}
247 9a06d96f Olga Brani
248 8fb8d0cf Giorgos Korfiatis
249 9efcce2a Sofia Papagiannaki
def get_properties(obj):
250 2f732a9b Sofia Papagiannaki
    def get_class_attr(_class, attr):
251 2f732a9b Sofia Papagiannaki
        try:
252 2f732a9b Sofia Papagiannaki
            return getattr(_class, attr)
253 2f732a9b Sofia Papagiannaki
        except AttributeError:
254 2f732a9b Sofia Papagiannaki
            return
255 64492c49 Kostas Papadimitriou
256 8fb8d0cf Giorgos Korfiatis
    return (i for i in vars(obj.__class__)
257 8fb8d0cf Giorgos Korfiatis
            if isinstance(get_class_attr(obj.__class__, i), property))
258 8fb8d0cf Giorgos Korfiatis
259 9a06d96f Olga Brani
260 68a8935f Giorgos Korfiatis
def model_to_dict(obj, exclude=None, include_empty=True):
261 9a06d96f Olga Brani
    '''
262 9a06d96f Olga Brani
        serialize model object to dict with related objects
263 9a06d96f Olga Brani

264 9a06d96f Olga Brani
        author: Vadym Zakovinko <vp@zakovinko.com>
265 9a06d96f Olga Brani
        date: January 31, 2011
266 9a06d96f Olga Brani
        http://djangosnippets.org/snippets/2342/
267 9a06d96f Olga Brani
    '''
268 68a8935f Giorgos Korfiatis
269 68a8935f Giorgos Korfiatis
    if exclude is None:
270 68a8935f Giorgos Korfiatis
        exclude = ['AutoField', 'ForeignKey', 'OneToOneField']
271 9a06d96f Olga Brani
    tree = {}
272 9a06d96f Olga Brani
    for field_name in obj._meta.get_all_field_names():
273 9a06d96f Olga Brani
        try:
274 9a06d96f Olga Brani
            field = getattr(obj, field_name)
275 9a06d96f Olga Brani
        except (ObjectDoesNotExist, AttributeError):
276 9a06d96f Olga Brani
            continue
277 9a06d96f Olga Brani
278 8fb8d0cf Giorgos Korfiatis
        if field.__class__.__name__ in ['RelatedManager',
279 8fb8d0cf Giorgos Korfiatis
                                        'ManyRelatedManager']:
280 9a06d96f Olga Brani
            if field.model.__name__ in exclude:
281 9a06d96f Olga Brani
                continue
282 9a06d96f Olga Brani
283 9a06d96f Olga Brani
            if field.__class__.__name__ == 'ManyRelatedManager':
284 9a06d96f Olga Brani
                exclude.append(obj.__class__.__name__)
285 9a06d96f Olga Brani
            subtree = []
286 9a06d96f Olga Brani
            for related_obj in getattr(obj, field_name).all():
287 9a06d96f Olga Brani
                value = model_to_dict(related_obj, exclude=exclude)
288 9a06d96f Olga Brani
                if value or include_empty:
289 9a06d96f Olga Brani
                    subtree.append(value)
290 9a06d96f Olga Brani
            if subtree or include_empty:
291 9a06d96f Olga Brani
                tree[field_name] = subtree
292 9a06d96f Olga Brani
            continue
293 9a06d96f Olga Brani
294 9a06d96f Olga Brani
        field = obj._meta.get_field_by_name(field_name)[0]
295 9a06d96f Olga Brani
        if field.__class__.__name__ in exclude:
296 9a06d96f Olga Brani
            continue
297 9a06d96f Olga Brani
298 9a06d96f Olga Brani
        if field.__class__.__name__ == 'RelatedObject':
299 9a06d96f Olga Brani
            exclude.append(field.model.__name__)
300 9a06d96f Olga Brani
            tree[field_name] = model_to_dict(getattr(obj, field_name),
301 9a06d96f Olga Brani
                                             exclude=exclude)
302 9a06d96f Olga Brani
            continue
303 9a06d96f Olga Brani
304 9a06d96f Olga Brani
        value = getattr(obj, field_name)
305 9a06d96f Olga Brani
        if field.__class__.__name__ == 'ForeignKey':
306 9a06d96f Olga Brani
            value = unicode(value) if value is not None else value
307 9a06d96f Olga Brani
        if value or include_empty:
308 9a06d96f Olga Brani
            tree[field_name] = value
309 9efcce2a Sofia Papagiannaki
    properties = list(get_properties(obj))
310 9efcce2a Sofia Papagiannaki
    for p in properties:
311 8fb8d0cf Giorgos Korfiatis
        tree[p] = getattr(obj, p)
312 9efcce2a Sofia Papagiannaki
    tree['str_repr'] = obj.__str__()
313 9a06d96f Olga Brani
314 9a06d96f Olga Brani
    return tree
315 6936103e Kostas Papadimitriou
316 8fb8d0cf Giorgos Korfiatis
317 6936103e Kostas Papadimitriou
def login_url(request):
318 6936103e Kostas Papadimitriou
    attrs = {}
319 6936103e Kostas Papadimitriou
    for attr in ['login', 'key', 'code']:
320 6936103e Kostas Papadimitriou
        val = request.REQUEST.get(attr, None)
321 6936103e Kostas Papadimitriou
        if val:
322 6936103e Kostas Papadimitriou
            attrs[attr] = val
323 6936103e Kostas Papadimitriou
    return "%s?%s" % (reverse('login'), urllib.urlencode(attrs))
324 440f7c0c Kostas Papadimitriou
325 440f7c0c Kostas Papadimitriou
326 440f7c0c Kostas Papadimitriou
def redirect_back(request, default='index'):
327 440f7c0c Kostas Papadimitriou
    """
328 440f7c0c Kostas Papadimitriou
    Redirect back to referer if safe and possible.
329 440f7c0c Kostas Papadimitriou
    """
330 440f7c0c Kostas Papadimitriou
    referer = request.META.get('HTTP_REFERER')
331 440f7c0c Kostas Papadimitriou
332 fb9ba8d5 Kostas Papadimitriou
    safedomain = settings.BASE_URL.replace("https://", "").replace(
333 440f7c0c Kostas Papadimitriou
        "http://", "")
334 440f7c0c Kostas Papadimitriou
    safe = restrict_next(referer, safedomain)
335 440f7c0c Kostas Papadimitriou
    # avoid redirect loop
336 440f7c0c Kostas Papadimitriou
    loops = referer == request.get_full_path()
337 440f7c0c Kostas Papadimitriou
    if referer and safe and not loops:
338 440f7c0c Kostas Papadimitriou
        return redirect(referer)
339 440f7c0c Kostas Papadimitriou
    return redirect(reverse(default))
340 9efd0075 Kostas Papadimitriou
341 9efd0075 Kostas Papadimitriou
342 9efd0075 Kostas Papadimitriou
def truncatename(v, max=18, append="..."):
343 9efd0075 Kostas Papadimitriou
    length = len(v)
344 9efd0075 Kostas Papadimitriou
    if length > max:
345 9efd0075 Kostas Papadimitriou
        return v[:max] + append
346 9efd0075 Kostas Papadimitriou
    else:
347 9efd0075 Kostas Papadimitriou
        return v