summaryrefslogtreecommitdiffstats
path: root/core/middleware.py
blob: fe68fb2d5d9613f0868571c68b10e14bd5873d3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import pathlib 
from django import http
from django.conf import settings
from django.urls import Resolver404, resolve
from django.utils.deprecation import MiddlewareMixin
from troggle import settings

"""Non-standard django middleware is loaded from this file.

"""
todo = """SmartAppendSlashMiddleware(object) Not Working. 
It needs re-writing. Can we make this work even though we have a catchall url rule ?
"""


class TroggleAppendSlashMiddleware(MiddlewareMixin):
    """
    "SmartAppendSlash" middleware for taking care of URL rewriting.

    This middleware appends a missing slash, if:
    * the SMART_APPEND_SLASH setting is True
    * the URL without the slash does not exist in urls.py
    * the URL with an appended slash does exist in urls.py
    Otherwise it won't touch the URL.
    
    MODIFICATION
    Since we have a universal catchall url pattern in urls.py, the usual way this works
    won't ever trigger adding a slash. So we check for the existence of a file in expoweb,
    not the existence of a pattern in urls.py...
    
    but site_media..
    but css etc....
    
    CONCLUSION
    This technique "works" but would be a maintence nightmare, so DO NOT USE IT
    do NOT include 
    troggle.core.middleware.TroggleAppendSlashMiddleware
    in settings.py 
    
    FURTHER WARNING
    If playing about with this, the 301 redirects that it creates will be cached INDEFINITELY by any browser you
    used to test it, e.g. /css/main.css with be permanetly redirected to /css/main2.css/ with dreadful 
    consequences, similarly for any images visited. You have to go into your browser settings and delete all cached
    files to recover from this.
    """

    def process_request(self, request):
        """Called for every url so return as quickly as possible
        Append a slash if TROGGLE_APPEND_SLASH is set, the resulting URL resolves and it doesn't without the /
        """
        if not settings.TROGGLE_APPEND_SLASH:
            return None

        if request.path.endswith("/"):
            return None

        if request.path.endswith("_edit"):
            return None
        
        if request.path.startswith("/"):
            relative_path = request.path[1:]
        else:
            relative_path = request.path
            
        for root in [settings.MEDIA_ROOT, settings.JSLIB_ROOT, settings.EXPOFILES, settings.SCANS_ROOT, settings.PHOTOS_ROOT]:
            full_path = root / relative_path
            print(f"+++++ MIDDLEWARE checking {root} / {relative_path} ")
            if full_path.is_file():
                print(f"+++++ MIDDLEWARE It IS a {root} file {full_path=} so use it as-is.")
                return None
            else:
                print(f"+++++ MIDDLEWARE NOT a {root}file {full_path=}")       

        host = http.HttpRequest.get_host(request)
        old_url = [host, request.path]
        # if _resolves(old_url[1]):
            # return None

        # So: it does not resolve according to our criteria, i.e. _edit doesn't count, and URL resolves doesn't count because of the catch all
        new_url = old_url[:]
        new_url[1] = new_url[1] + "/"
        if not _resolves(new_url[1]):
            print(f"+++++ MIDDLEWARE add SLASH and resolves {old_url=} => {new_url=}")
            return None
        else:
            if settings.DEBUG and request.method == "POST":
                # replace this exception with a redirect to an error page
                raise RuntimeError(
                    f"You called this URL via POST, but the URL doesn't end in a slash and you have SMART_APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to {new_url[0]}{new_url[1]} (note the trailing slash), or set SMART_APPEND_SLASH=False in your Django settings."
                )
        if new_url != old_url:
            # Redirect
            if new_url[0]:
                newurl = f"{request.is_secure() and 'https' or 'http'}://{new_url[0]}{new_url[1]}"
            else:
                newurl = new_url[1]
            if request.GET:
                newurl += "?" + request.GET.urlencode()
            return http.HttpResponsePermanentRedirect(newurl)

        return None


def _resolves(url):
    try:
        # If the URL does not resolve, the function raises a Resolver404 exception (a subclass of Http404)
        resolve(url)
        # this will ALWAYS be resolved by expopages because it will produce pagenotfound if not the thing asked for
        # so handle this in expopages, not in middleware
        return True
    except Resolver404:
        return False
    except:
        print(url)
        raise