passerelle/passerelle/urls_utils.py

130 lines
4.4 KiB
Python

# Decorating URL includes, <https://djangosnippets.org/snippets/2532/>
from functools import wraps
from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
from django.views.debug import technical_404_response
from django.http import Http404
class DecoratedURLPattern(RegexURLPattern):
def resolve(self, *args, **kwargs):
result = super(DecoratedURLPattern, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
class DecoratedRegexURLResolver(RegexURLResolver):
def resolve(self, *args, **kwargs):
result = super(DecoratedRegexURLResolver, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
def decorated_includes(func, includes, *args, **kwargs):
urlconf_module, app_name, namespace = includes
for item in urlconf_module:
if isinstance(item, RegexURLPattern):
item.__class__ = DecoratedURLPattern
item._decorate_with = func
elif isinstance(item, RegexURLResolver):
item.__class__ = DecoratedRegexURLResolver
item._decorate_with = func
return urlconf_module, app_name, namespace
# below, a set of decorators to generate urls.py like this:
# urlpatterns = required(
# app_enabled('applabel'),
# [... urls for applabel ...]
# )
def unless(test, message):
'''Decorator returning a 404 status code if some condition is not met'''
def decorator(func):
@wraps(func)
def f(request, *args, **kwargs):
if not test():
return technical_404_response(request, Http404(message))
return func(request, *args, **kwargs)
return f
return decorator
def app_enabled(app_label):
'''for enabling a view based on PASSERELLE_APP_<APP_LABEL>_ENABLED flag'''
def test():
return getattr(settings, 'PASSERELLE_APP_%s_ENABLED' % app_label.upper(), True)
return unless(test, 'please enable %s' % app_label)
def setting_enabled(name):
'''for enabling a view based on a setting'''
def test():
return getattr(settings, name, False)
return unless(test, 'please enable %s' % name)
# code bellow is borrowed from https://djangosnippets.org/snippets/2607/
# or https://gist.github.com/sjzabel/1378003
def required(wrapping_functions,patterns_rslt):
'''
Used to require 1..n decorators in any view returned by a url tree
Usage:
urlpatterns = required(func,[...urls...])
urlpatterns = required((func,func,func),[...urls...])
Note:
Use functools.partial to pass keyword params to the required
decorators. If you need to pass args you will have to write a
wrapper function.
Example:
from functools import partial
urlpatterns = required(
partial(login_required,login_url='/accounts/login/'),
[...urls...]
)
'''
if not hasattr(wrapping_functions, '__iter__'):
wrapping_functions = (wrapping_functions, )
return [
_wrap_instance__resolve(wrapping_functions, instance)
for instance in patterns_rslt
]
def _wrap_instance__resolve(wrapping_functions, instance):
def _wrap_func_in_returned_resolver_match(*args, **kwargs):
rslt = resolve(*args, **kwargs)
if not hasattr(rslt, 'func'):
return rslt
f = getattr(rslt, 'func')
for _f in reversed(wrapping_functions):
# @decorate the function from inner to outter
f = _f(f)
setattr(rslt, 'func', f)
return rslt
if not hasattr(instance, 'resolve'):
return instance
resolve = getattr(instance, 'resolve')
setattr(instance, 'resolve', _wrap_func_in_returned_resolver_match)
return instance
def manager_required(function=None, login_url=None):
def check_manager(user):
if user and user.is_staff:
return True
if user and not user.is_anonymous:
raise PermissionDenied()
# As the last resort, show the login form
return False
actual_decorator = user_passes_test(check_manager, login_url=login_url)
if function:
return actual_decorator(function)
return actual_decorator