+import functools
+
+from django.http import HttpResponseForbidden
+from django.shortcuts import get_object_or_404
+
+from .models import XGroup, Membership
+
+
+def with_group(view_func):
+ """Converts a group slug into a XGroup object.
+
+ Example:
+ >>> @with_group
+ ... def my_view(request, group, *args, **kwargs):
+ ... # group is now a XGroup instance
+ """
+
+ @functools.wraps(view_func)
+ def wrapped_view_func(request, group_slug, *args, **kwargs):
+ group = get_object_or_404(XGroup, short=group_slug)
+
+ request.group = group
+
+ return view_func(request, group, *args, **kwargs)
+
+ return wrapped_view_func
+
+
+class group_required(object):
+ """Force a minimum group membership level.
+
+ Also combines with_group features:
+ - fetches the XGroup object,
+ - sets request.group
+ - sets request.membership
+
+ Usage:
+
+ >>> @group_required(level=Membership.LEVEL_MEMBER)
+ ... def my_view(request, group, *args, **kwargs):
+ ... pass
+ """
+
+ def __init__(self, level=Membership.LEVEL_MEMBER):
+ self.level = level
+
+ def __call__(self, view_func):
+ @functools.wraps(view_func)
+ def wrapped_view_func(request, group_slug, *args, **kwargs):
+ group = get_object_or_404(XGroup, short=group_slug)
+ if not request.user.is_authenticated():
+ return HttpResponseForbidden()
+
+ try:
+ membership = request.user.memberships.get(
+ xgroup=group,
+ level__gte=self.level,
+ state='enabled',
+ )
+ except Membership.DoesNotExist:
+ membership = None
+
+ request.group = group
+ request.membership = membership
+
+ if membership:
+ return view_func(request, group, *args, membership=membership, **kwargs)
+ else:
+ return HttpResponseForbidden()
+ return wrapped_view_func