course_nav.py 5.57 KB
Newer Older
1
import re
2
from urlparse import urlparse
3 4 5 6

from django.http import Http404
from django.shortcuts import redirect

7
from wiki.models import reverse as wiki_reverse
8
from courseware.access import has_access
9
from courseware.courses import get_course_with_access
10 11


12 13
IN_COURSE_WIKI_REGEX = r'/courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/wiki/(?P<wiki_path>.*|)$'

Calen Pennington committed
14

15 16 17 18 19 20 21
class Middleware(object):
    """
    This middleware is to keep the course nav bar above the wiki while
    the student clicks around to other wiki pages.
    If it intercepts a request for /wiki/.. that has a referrer in the
    form /courses/course_id/... it will redirect the user to the page
    /courses/course_id/wiki/...
Calen Pennington committed
22

23 24 25
    It is also possible that someone followed a link leading to a course
    that they don't have access to. In this case, we redirect them to the
    same page on the regular wiki.
Calen Pennington committed
26

27 28
    If we return a redirect, this middleware makes sure that the redirect
    keeps the student in the course.
Calen Pennington committed
29

30 31 32
    Finally, if the student is in the course viewing a wiki, we change the
    reverse() function to resolve wiki urls as a course wiki url by setting
    the _transform_url attribute on wiki.models.reverse.
Calen Pennington committed
33

34
    Forgive me Father, for I have hacked.
35
    """
Calen Pennington committed
36

37 38
    def __init__(self):
        self.redirected = False
Calen Pennington committed
39

40 41 42
    def process_request(self, request):
        self.redirected = False
        wiki_reverse._transform_url = lambda url: url
Calen Pennington committed
43

44 45
        referer = request.META.get('HTTP_REFERER')
        destination = request.path
Calen Pennington committed
46 47


48 49
        if request.method == 'GET':
            new_destination = self.get_redirected_url(request.user, referer, destination)
Calen Pennington committed
50

51 52 53 54
            if new_destination != destination:
                # We mark that we generated this redirection, so we don't modify it again
                self.redirected = True
                return redirect(new_destination)
Calen Pennington committed
55

56 57 58 59 60
        course_match = re.match(IN_COURSE_WIKI_REGEX, destination)
        if course_match:
            course_id = course_match.group('course_id')
            prepend_string = '/courses/' + course_match.group('course_id')
            wiki_reverse._transform_url = lambda url: prepend_string + url
Calen Pennington committed
61

62
        return None
Calen Pennington committed
63 64


65 66 67 68 69
    def process_response(self, request, response):
        """
        If this is a redirect response going to /wiki/*, then we might need
        to change it to be a redirect going to /courses/*/wiki*.
        """
Calen Pennington committed
70
        if not self.redirected and response.status_code == 302:   # This is a redirect
71 72 73
            referer = request.META.get('HTTP_REFERER')
            destination_url = response['LOCATION']
            destination = urlparse(destination_url).path
Calen Pennington committed
74

75
            new_destination = self.get_redirected_url(request.user, referer, destination)
Calen Pennington committed
76

77 78 79
            if new_destination != destination:
                new_url = destination_url.replace(destination, new_destination)
                response['LOCATION'] = new_url
Calen Pennington committed
80

81
        return response
Calen Pennington committed
82 83


84 85 86 87 88 89 90
    def get_redirected_url(self, user, referer, destination):
        """
        Returns None if the destination shouldn't be changed.
        """
        if not referer:
            return destination
        referer_path = urlparse(referer).path
Calen Pennington committed
91

92
        path_match = re.match(r'^/wiki/(?P<wiki_path>.*|)$', destination)
93 94 95 96 97
        if path_match:
            # We are going to the wiki. Check if we came from a course
            course_match = re.match(r'/courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/.*', referer_path)
            if course_match:
                course_id = course_match.group('course_id')
Calen Pennington committed
98

99 100
                # See if we are able to view the course. If we are, redirect to it
                try:
101 102
                    course = get_course_with_access(user, course_id, 'load')
                    return "/courses/" + course.id + "/wiki/" + path_match.group('wiki_path')
103 104 105
                except Http404:
                    # Even though we came from the course, we can't see it. So don't worry about it.
                    pass
Calen Pennington committed
106

107
        else:
Calen Pennington committed
108
            # It is also possible we are going to a course wiki view, but we
109
            # don't have permission to see the course!
110
            course_match = re.match(IN_COURSE_WIKI_REGEX, destination)
111 112 113 114
            if course_match:
                course_id = course_match.group('course_id')
                # See if we are able to view the course. If we aren't, redirect to regular wiki
                try:
115
                    course = get_course_with_access(user, course_id, 'load')
116
                    # Good, we can see the course. Carry on
117
                    return destination
118 119
                except Http404:
                    # We can't see the course, so redirect to the regular wiki
120
                    return "/wiki/" + course_match.group('wiki_path')
Calen Pennington committed
121

122
        return destination
Calen Pennington committed
123

124

125 126 127 128 129 130 131 132
def context_processor(request):
    """
    This is a context processor which looks at the URL while we are
    in the wiki. If the url is in the form
    /courses/(course_id)/wiki/...
    then we add 'course' to the context. This allows the course nav
    bar to be shown.
    """
Calen Pennington committed
133

134
    match = re.match(IN_COURSE_WIKI_REGEX, request.path)
135 136
    if match:
        course_id = match.group('course_id')
Calen Pennington committed
137

138
        try:
139
            course = get_course_with_access(request.user, course_id, 'load')
140
            staff_access = has_access(request.user, course, 'staff')
Calen Pennington committed
141
            return {'course': course,
142
                    'staff_access': staff_access}
143 144 145 146 147
        except Http404:
            # We couldn't access the course for whatever reason. It is too late to change
            # the URL here, so we just leave the course context. The middleware shouldn't
            # let this happen
            pass
Calen Pennington committed
148

149
    return {}