From 5d4ad93c5113a7895e98556f741714e2652f05b9 Mon Sep 17 00:00:00 2001
From: Philip Sargent <philip.sargent@klebos.com>
Date: Sat, 10 Apr 2021 15:30:29 +0100
Subject: [PATCH] Better FileNotFound in expofiles

---
 core/views/expo.py                         | 60 +++++++++++++++-------
 templates/{flatpage.html => expopage.html} |  0
 urls.py                                    |  1 +
 3 files changed, 42 insertions(+), 19 deletions(-)
 rename templates/{flatpage.html => expopage.html} (100%)

diff --git a/core/views/expo.py b/core/views/expo.py
index d74120f..0a95011 100644
--- a/core/views/expo.py
+++ b/core/views/expo.py
@@ -50,27 +50,40 @@ default_head = '''<head>
 
 def expofiles_redirect(request, path):
     '''This is used only when running as a test system without a local copy of /expofiles/
+    when settings.EXPOFILESREMOTE is True
     '''
     return redirect(urljoin('http://expo.survex.com/expofiles/', path))
 
 def expofilessingle(request, filepath):
-    '''sends a single binary file to the user,
+    '''sends a single binary file to the user, if not found, show the parent directory
+    If the path actually is a directory, then show that.
     '''
     fn=urlunquote(filepath)
     fn = Path(settings.EXPOFILES,filepath)
     if fn.is_dir():
         return expofilesdir(request, Path(fn), Path(filepath))
-    # print(" - expofilessingle {}:{}:{}:".format(filepath, fn, getmimetype(fn)))
-    return HttpResponse(content=open(fn, "rb"),content_type=getmimetype(filepath)) # any file
+    if fn.is_file():
+        return HttpResponse(content=open(fn, "rb"),content_type=getmimetype(filepath)) # any file
+    else:
+        # not a file, so show parent directory
+        return expofilesdir(request, Path(fn).parent, Path(filepath).parent)
 
 def expofilesdir(request, dirpath, filepath):
     '''does a directory display. If there is an index.html file we should display that.
-    - dirpath is a Path() and it does not have /expofiles/ in it
+    - dirpath is a full Path() resolved including lcoal machine /expofiles/
+    - filepath is a Path() and it does not have /expofiles/ in it
     '''
-    # print(" - expofilesdir {}".format(dirpath))
+    # print(f' - expofilesdir {dirpath}')
     urlpath = 'expofiles' / Path(filepath)
+    try:
+        for f in dirpath.iterdir():
+            pass
+    except FileNotFoundError:
+        print(f' - expofilesdir {dirpath}')
+        return expofilesdir(request, dirpath.parent, filepath.parent)
+        
     fileitems = []
-    diritems = []
+    diritems = []    
     for f in dirpath.iterdir():
         if f.is_dir():
              diritems.append((urlpath / f.parts[-1], str(f.parts[-1])))
@@ -113,7 +126,7 @@ def expowebpage(request, expowebpath, path):
     menumatch = re.match(r'(.*)<ul id="links">', body, re.DOTALL + re.IGNORECASE)
     if menumatch:
         has_menu = True
-    return render(request, 'flatpage.html', {'editable': editable, 'path': path, 'title': title, 
+    return render(request, 'expopage.html', {'editable': editable, 'path': path, 'title': title, 
                 'body': body, 'homepage': (path == "index.htm"), 'has_menu': has_menu})
  
 def mediapage(request, subpath=None, doc_root=None):
@@ -179,6 +192,10 @@ def expopage(request, path):
  
 
 def getmimetype(path):
+    '''Our own version rather than relying on what is provided by the python library. Note that when
+    Apache or nginx is used to deliver /expofiles/ it will use it's own idea of mimetypes and
+    not these.
+    '''
     path = str(path)
     if path.lower().endswith(".css"): return "text/css"
     if path.lower().endswith(".txt"): return "text/css"
@@ -210,13 +227,16 @@ def getmimetype(path):
 @login_required_if_public
 @ensure_csrf_cookie
 def editexpopage(request, path):
+    '''Manages the 'Edit this Page' capability for expo handbook and other html pages.
+    Relies on javascript to provide the in-browser editing environment.
+    '''
     try:
+        # if a cave not a webpage at all. 
         r = Cave.objects.get(url = path)
         return troggle.core.views.caves.editCave(request, r.cave.slug)
     except Cave.DoesNotExist:
         pass
 
-
     try:
         filepath = Path(settings.EXPOWEB) / path
         o = open(filepath, "r")
@@ -242,24 +262,24 @@ def editexpopage(request, path):
         
 
     if request.method == 'POST': # If the form has been submitted...
-        flatpageForm = FlatPageForm(request.POST) # A form bound to the POST data
-        if flatpageForm.is_valid():# Form valid therefore write file
-            print("### \n", str(flatpageForm)[0:300])
+        pageform = ExpoPageForm(request.POST) # A form bound to the POST data
+        if pageform.is_valid():# Form valid therefore write file
+            print("### \n", str(pageform)[0:300])
             print("### \n csrfmiddlewaretoken: ",request.POST['csrfmiddlewaretoken'])
             if filefound:
                 headmatch =  re.match(r"(.*)<title>.*</title>(.*)", head, re.DOTALL + re.IGNORECASE)
                 if headmatch:
-                    head = headmatch.group(1) + "<title>" + flatpageForm.cleaned_data["title"] + "</title>" + headmatch.group(2)
+                    head = headmatch.group(1) + "<title>" + pageform.cleaned_data["title"] + "</title>" + headmatch.group(2)
                 else:
-                    head = "<title>" + flatpageForm.cleaned_data["title"] + "</title>"
+                    head = "<title>" + pageform.cleaned_data["title"] + "</title>"
             else:
-                head = "<title>" + flatpageForm.cleaned_data["title"] + "</title>"
+                head = "<title>" + pageform.cleaned_data["title"] + "</title>"
                 preheader = "<html>"
                 headerargs = ""
                 postheader = ""
                 bodyargs = ""
                 postbody = "</html>" 
-            body = flatpageForm.cleaned_data["html"]
+            body = pageform.cleaned_data["html"]
             body = body.replace("\r", "")
             result = "%s<head%s>%s</head>%s<body%s>\n%s</body>%s" % (preheader, headerargs, head, postheader, bodyargs, body, postbody)
             f = open(filepath, "w")
@@ -273,13 +293,15 @@ def editexpopage(request, path):
                 title, = m.groups()
             else:
                 title = ""
-            flatpageForm = FlatPageForm({"html": body, "title": title})
+            pageform = ExpoPageForm({"html": body, "title": title})
         else:
             body = "### File not found ###\n"    + str(filepath)
-            flatpageForm = FlatPageForm({"html": body, "title": "Missing"})
-    return render(request, 'editexpopage.html', {'path': path, 'form': flatpageForm, })
+            pageform = ExpoPageForm({"html": body, "title": "Missing"})
+    return render(request, 'editexpopage.html', {'path': path, 'form': pageform, })
 
-class FlatPageForm(forms.Form):
+class ExpoPageForm(forms.Form):
+    '''The form used by the editexpopage function
+    '''
     title = forms.CharField(widget=forms.TextInput(attrs={'size':'60'}))
 
     #html = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 20}))
diff --git a/templates/flatpage.html b/templates/expopage.html
similarity index 100%
rename from templates/flatpage.html
rename to templates/expopage.html
diff --git a/urls.py b/urls.py
index 7bd6585..7184823 100644
--- a/urls.py
+++ b/urls.py
@@ -172,6 +172,7 @@ urlpatterns = [
 ]
 
 # When apache is running these prempt Django so Django never sees them.
+# NB apache has its own ideas about mimetypes, so behaviour may not be identical for .xml files by troggle
 
 # NEW apache configurations suggested as of 2 April 2021: 
 # Alias /site-media/ /home/expo/troggle/media/