2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2024-11-21 14:51:51 +00:00

Revert "update oddity with VS Code and WSL2"

This reverts commit 41b2bcee4f.

CR LF != LF issue
This commit is contained in:
Philip Sargent 2022-04-05 10:37:31 +03:00
parent 41b2bcee4f
commit 71ed0815cc
6 changed files with 711 additions and 711 deletions

View File

@ -1,3 +1,3 @@
The copy in this /_deploy/ folder may not be the latest if active development
has been going on in the parent folder. Check there for a later copy of
The copy in this /_deploy/ folder may not be the latest if active development
has been going on in the parent folder. Check there for a later copy of
the localsettingsWSL file.

View File

@ -1,368 +1,368 @@
import os
import re
import subprocess
from pathlib import Path
from urllib.parse import urljoin, unquote as urlunquote
from urllib.request import urlopen
from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.urls import reverse, resolve
from django.template import Context, loader
from django.views.decorators.csrf import ensure_csrf_cookie
from django.contrib import admin
import django.forms as forms
from .auth import login_required_if_public
from troggle.core.models.caves import Cave
import troggle.core.views.caves
import troggle.settings as settings
'''Formerly a separate package called 'flatpages' written by Martin Green 2011.
This was NOT django.contrib.flatpages which stores HTML in the database, so the name was changed to expopages.
Then it was incorporated into troggle directly, rather than being an unnecessary external package.
'''
default_head = '''<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>CUCC Expedition - index</title>
<link rel="stylesheet" type="text/css" href="../css/main2.css" />
<link rel="stylesheet" type="text/css" href="../../css/main2.css" />
<link rel="stylesheet" type="text/css" href="../../../css/main2.css" />
</head>
<body>
<h1>Expo</h1>
<h2 id="tophead">CUCC Expedition</h2>
<ul id="links">
<li><a href="/index.htm">Home</a></li>
<li><a href="/infodx.htm">Main Index</a></li>
<li><a href="/handbook/index.htm">Handbook</a></li>
<li><a href="/handbook/computing/onlinesystems.html">Online systems</a></li>
<li><a href="/pubs.htm">Reports</a></li>
<li><a href="/areas.htm">Areas</a></li>
<li><a href="/caves">Caves</a></li>
<li><a href="/expedition/2019">Troggle</a></li>
<li><form name=P method=get action="/search" target="_top">
<input id="omega-autofocus" type=search name=P size=8 autofocus>
<input type=submit value="Search"></li>
</ul>''' # this gets overwritten by templates/menu.html by django for most normal pages
def expofiles_redirect(request, filepath):
'''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/', filepath))
def map(request):
'''Serves unadorned the expoweb/map/map.html file
'''
fn = Path(settings.EXPOWEB, 'map', 'map.html')
return HttpResponse(content=open(fn, "r"),content_type='text/html')
def mapfile(request, path):
'''Serves unadorned file
'''
fn = Path(settings.EXPOWEB, 'map', path)
return HttpResponse(content=open(fn, "r"),content_type=getmimetype(fn))
def expofilessingle(request, filepath):
'''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.
'''
#print(f' - expofilessingle {filepath}')
if filepath =="" or filepath =="/":
return expofilesdir(request, settings.EXPOFILES, "")
fn=urlunquote(filepath)
fn = Path(settings.EXPOFILES,filepath)
if fn.is_dir():
return expofilesdir(request, Path(fn), Path(filepath))
if fn.is_file():
return HttpResponse(content=open(fn, "rb"),content_type=getmimetype(filepath)) # any file
else:
# not a file, so show parent directory - DANGER need to check this is limited to below expofiles
if Path(fn).parent == Path(settings.EXPOFILES).parent:
return expofilesdir(request, Path(settings.EXPOFILES), Path(filepath).parent)
else:
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 full Path() resolved including local machine /expofiles/
- filepath is a Path() and it does not have /expofiles/ in it
'''
#print(f' - expofilesdir {dirpath} settings.EXPOFILESREMOTE: {settings.EXPOFILESREMOTE}')
if filepath:
urlpath = 'expofiles' / Path(filepath)
else:
urlpath = Path('expofiles')
try:
for f in dirpath.iterdir():
pass
except FileNotFoundError:
#print(f' - expofilesdir error {dirpath}')
return expofilesdir(request, dirpath.parent, filepath.parent)
fileitems = []
diritems = []
for f in dirpath.iterdir():
if f.is_dir():
diritems.append((urlpath / f.parts[-1], str(f.parts[-1])))
else:
# if f.parts[-1].lower() == 'index.htm' or f.parts[-1].lower() == 'index.html': # css cwd problem
# return HttpResponse(content=open(f, "rb"),content_type=getmimetype(filepath)) # any file
# return expofilessingle(request, str(Path(filepath / f.parts[-1])))
fileitems.append((Path(urlpath) / f.parts[-1], str(f.parts[-1]), getmimetype(f)))
return render(request, 'dirdisplay.html', { 'filepath': urlpath, 'fileitems':fileitems, 'diritems': diritems,'settings': settings })
def expowebpage(request, expowebpath, path):
'''Adds menus and serves an HTML page
'''
if not Path(expowebpath / path).is_file():
# Should not get here if the path has suffix "_edit"
print(f' - 404 error in expowebpage() {path}')
return render(request, 'pagenotfound.html', {'path': path}, status="404")
try:
with open(os.path.normpath(expowebpath / path), "r") as o:
html = o.read()
except:
try:
with open(os.path.normpath(expowebpath / path), "rb") as o:
html = str(o.read()).replace("<h1>","<h1>BAD NON-UTF-8 characters here - ")
except:
return HttpResponse(default_head + '<h3>UTF-8 Parsing Failure:<br>Page could not be parsed using UTF-8:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to replace dubious umlauts and &pound; symbols with correct HTML entities e.g. <em>&amp;pound;;</em>. </body' )
m = re.search(r'(.*)<\s*head([^>]*)>(.*)<\s*/head\s*>(.*)<\s*body([^>]*)>(.*)<\s*/body\s*>(.*)', html, re.DOTALL + re.IGNORECASE)
if m:
preheader, headerattrs, head, postheader, bodyattrs, body, postbody = m.groups()
else:
return HttpResponse(default_head + html + '<h3>HTML Parsing failure:<br>Page could not be parsed into header and body:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to be in the expected full HTML format </body' )
m = re.search(r"<title>(.*)</title>", head, re.DOTALL + re.IGNORECASE)
if m:
title, = m.groups()
else:
title = ""
m = re.search(r"^<meta([^>]*)noedit", head, re.DOTALL + re.IGNORECASE)
if m:
editable = False
else:
editable = os.access(Path(expowebpath / path), os.W_OK) # are file permissions writeable?
has_menu = False
menumatch = re.match(r'(.*)<div id="menu">', body, re.DOTALL + re.IGNORECASE)
if menumatch:
has_menu = True
menumatch = re.match(r'(.*)<ul id="links">', body, re.DOTALL + re.IGNORECASE)
if menumatch:
has_menu = True
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):
'''This is for special prefix paths /photos/ /site_media/, /static/ etc.
as defined in urls.py . If given a directory, gives a failure page.
'''
#print(" - XXXXX_ROOT: {} ...{}".format(doc_root, subpath))
if doc_root is not None:
filetobeopened = Path(doc_root, subpath)
if filetobeopened.is_dir():
return render(request, 'nodirlist.html', {'path': subpath})
try:
return HttpResponse(content=open(filetobeopened, "rb"), content_type=getmimetype(subpath))
except IOError:
return render(request, 'pagenotfound.html', {'path': subpath}, status="404")
else:
return render(request, 'pagenotfound.html', {'path': subpath}, status="404")
def expopage(request, path):
'''Either renders an HTML page from expoweb with all the menus,
or serves an unadorned binary file with mime type
'''
#print(" - EXPOPAGES delivering the file: '{}':{} as MIME type: {}".format(request.path, path,getmimetype(path)),flush=True)
if path.startswith("noinfo") and settings.PUBLIC_SITE and not request.user.is_authenticated:
return HttpResponseRedirect(urljoin(reverse("auth_login"),'?next={}'.format(request.path)))
if path.startswith("admin/"):
# don't even attempt to handle these sorts of mistakes
return HttpResponseRedirect("/admin/")
expowebpath = Path(settings.EXPOWEB)
if path == "":
return expowebpage(request, expowebpath, "index.htm")
if path.endswith(".htm") or path.endswith(".html"):
return expowebpage(request, expowebpath, path)
if Path(expowebpath / path ).is_dir():
for p in ["index.html", "index.htm"]:
if (expowebpath / path / p).is_file():
# This needs to reset the path to the new subdirectory
return HttpResponseRedirect('/'+str(Path(path) / p))
return render(request, 'pagenotfound.html', {'path': Path(path) / "index.html"}, status="404")
if path.endswith("/"):
# we already know it is not a directory.
# the final / may have been appended by middleware if there was no page without it
# do not redirect to a file path without the slash as we may get in a loop. Let the user fix it:
return render(request, 'dirnotfound.html', {'path': path, 'subpath': path[0:-1]})
# So it must be a file in /expoweb/ but not .htm or .html probably an image
filetobeopened = os.path.normpath(expowebpath / path)
try:
return HttpResponse(content=open(filetobeopened, "rb"), content_type=getmimetype(path))
except IOError:
return render(request, 'pagenotfound.html', {'path': path}, status="404")
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"
if path.lower().endswith(".js"): return "application/javascript"
if path.lower().endswith(".json"): return "application/javascript"
if path.lower().endswith(".ico"): return "image/vnd.microsoft.icon"
if path.lower().endswith(".png"): return "image/png"
if path.lower().endswith(".tif"): return "image/tif"
if path.lower().endswith(".gif"): return "image/gif"
if path.lower().endswith(".jpeg"): return "image/jpeg"
if path.lower().endswith(".jpg"): return "image/jpeg"
if path.lower().endswith("svg"): return "image/svg+xml"
if path.lower().endswith("xml"): return "application/xml" # we use "text/xhtml" for tunnel files
if path.lower().endswith(".pdf"): return "application/pdf"
if path.lower().endswith(".ps"): return "application/postscript"
if path.lower().endswith(".svx"): return "application/x-survex-svx"
if path.lower().endswith(".3d"): return "application/x-survex-3d"
if path.lower().endswith(".pos"): return "application/x-survex-pos"
if path.lower().endswith(".err"): return "application/x-survex-err"
if path.lower().endswith(".odt"): return "application/vnd.oasis.opendocument.text"
if path.lower().endswith(".ods"): return "application/vnd.oasis.opendocument.spreadsheet"
if path.lower().endswith(".docx"): return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
if path.lower().endswith(".xslx"): return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
if path.lower().endswith(".gz"): return "application/x-7z-compressed"
if path.lower().endswith(".7z"): return "application/x-7z-compressed"
if path.lower().endswith(".zip"): return "application/zip"
return ""
@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 HTML5 or 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")
html = o.read()
autogeneratedmatch = re.search(r"\<\!--\s*(.*?(Do not edit|It is auto-generated).*?)\s*--\>", html, re.DOTALL + re.IGNORECASE)
if autogeneratedmatch:
return HttpResponse(autogeneratedmatch.group(1))
m = re.search(r"(.*)<head([^>]*)>(.*)</head>(.*)<body([^>]*)>(.*)</body>(.*)", html, re.DOTALL + re.IGNORECASE)
if m:
filefound = True
preheader, headerargs, head, postheader, bodyargs, body, postbody = m.groups()
linksmatch = re.match(r'(.*)(<ul\s+id="links">.*)', body, re.DOTALL + re.IGNORECASE)
if linksmatch:
body, links = linksmatch.groups()
# if re.search(r"iso-8859-1", html):
# body = str(body, "iso-8859-1")
else:
return HttpResponse(default_head + html + '<h3>HTML Parsing failure:<br>Page could not be parsed into header and body:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to be in the expected full HTML format .</body' )
except IOError:
print("### File not found ### ", filepath)
filefound = False
if request.method == 'POST': # If the form has been submitted...
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>" + pageform.cleaned_data["title"] + "</title>" + headmatch.group(2)
else:
head = "<title>" + pageform.cleaned_data["title"] + "</title>"
else:
head = "<title>" + pageform.cleaned_data["title"] + "</title>"
preheader = "<html>"
headerargs = ""
postheader = ""
bodyargs = ""
postbody = "</html>\n"
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)
cwd = filepath.parent
filename = filepath.name
git = settings.GIT
# GIT see also core/models/cave.py writetrogglefile()
# GIT see also core/views/uploads.py dwgupload()
try:
with open(filepath, "w") as f:
print(f'WRITING{cwd}---{filename} ')
# as the wsgi process www-data, we have group write-access but are not owner, so cannot chmod.
# os.chmod(filepath, 0o664) # set file permissions to rw-rw-r--
f.write(result)
except PermissionError:
message = f'CANNOT save this file.\nPERMISSIONS incorrectly set on server for this file {filename}. Ask a nerd to fix this.'
return render(request,'errors/generic.html', {'message': message})
try:
cp_add = subprocess.run([git, "add", filename], cwd=cwd, capture_output=True, text=True)
if cp_add.returncode != 0:
msgdata = 'Ask a nerd to fix this.\n\n' + cp_add.stderr + '\n\n' + cp_add.stdout + '\n\nreturn code: ' + str(cp_add.returncode)
message = f'CANNOT git on server for this file {filename}. Edits saved but not added to git.\n\n' + msgdata
return render(request,'errors/generic.html', {'message': message})
cp_commit = subprocess.run([git, "commit", "-m", f'Troggle online: Edit this page - {filename}'], cwd=cwd, capture_output=True, text=True)
# This produces return code = 1 if it commits OK, but when the repo still needs to be pushed to origin/expoweb
if cp_commit.returncode != 0 and cp_commit.stdout != 'nothing to commit, working tree clean':
msgdata = 'Ask a nerd to fix this.\n\n' + cp_commit.stderr + '\n\n' + cp_commit.stdout + '\n\nreturn code: ' + str(cp_commit.returncode)
message = f'Error code with git on server for this file {filename}. Edits saved, added to git, but NOT committed.\n\n' + msgdata
return render(request,'errors/generic.html', {'message': message})
except subprocess.SubprocessError:
message = f'CANNOT git on server for this file {filename}. Subprocess error. Edits not saved.\nAsk a nerd to fix this.'
return render(request,'errors/generic.html', {'message': message})
return HttpResponseRedirect(reverse('expopage', args=[path])) # Redirect after POST
else:
if filefound:
m = re.search(r"<title>(.*)</title>", head, re.DOTALL + re.IGNORECASE)
if m:
title, = m.groups()
else:
title = ""
pageform = ExpoPageForm({"html": body, "title": title})
else:
body = "### File not found ###\n" + str(filepath)
pageform = ExpoPageForm({"html": body, "title": "Missing"})
return render(request, 'editexpopage.html', {'path': path, 'form': pageform, })
class ExpoPageForm(forms.Form):
'''The form used by the editexpopage function
'''
title = forms.CharField(widget=forms.TextInput(attrs={'size':'60'}))
html = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":20}))
import os
import re
import subprocess
from pathlib import Path
from urllib.parse import urljoin, unquote as urlunquote
from urllib.request import urlopen
from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.urls import reverse, resolve
from django.template import Context, loader
from django.views.decorators.csrf import ensure_csrf_cookie
from django.contrib import admin
import django.forms as forms
from .auth import login_required_if_public
from troggle.core.models.caves import Cave
import troggle.core.views.caves
import troggle.settings as settings
'''Formerly a separate package called 'flatpages' written by Martin Green 2011.
This was NOT django.contrib.flatpages which stores HTML in the database, so the name was changed to expopages.
Then it was incorporated into troggle directly, rather than being an unnecessary external package.
'''
default_head = '''<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>CUCC Expedition - index</title>
<link rel="stylesheet" type="text/css" href="../css/main2.css" />
<link rel="stylesheet" type="text/css" href="../../css/main2.css" />
<link rel="stylesheet" type="text/css" href="../../../css/main2.css" />
</head>
<body>
<h1>Expo</h1>
<h2 id="tophead">CUCC Expedition</h2>
<ul id="links">
<li><a href="/index.htm">Home</a></li>
<li><a href="/infodx.htm">Main Index</a></li>
<li><a href="/handbook/index.htm">Handbook</a></li>
<li><a href="/handbook/computing/onlinesystems.html">Online systems</a></li>
<li><a href="/pubs.htm">Reports</a></li>
<li><a href="/areas.htm">Areas</a></li>
<li><a href="/caves">Caves</a></li>
<li><a href="/expedition/2019">Troggle</a></li>
<li><form name=P method=get action="/search" target="_top">
<input id="omega-autofocus" type=search name=P size=8 autofocus>
<input type=submit value="Search"></li>
</ul>''' # this gets overwritten by templates/menu.html by django for most normal pages
def expofiles_redirect(request, filepath):
'''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/', filepath))
def map(request):
'''Serves unadorned the expoweb/map/map.html file
'''
fn = Path(settings.EXPOWEB, 'map', 'map.html')
return HttpResponse(content=open(fn, "r"),content_type='text/html')
def mapfile(request, path):
'''Serves unadorned file
'''
fn = Path(settings.EXPOWEB, 'map', path)
return HttpResponse(content=open(fn, "r"),content_type=getmimetype(fn))
def expofilessingle(request, filepath):
'''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.
'''
#print(f' - expofilessingle {filepath}')
if filepath =="" or filepath =="/":
return expofilesdir(request, settings.EXPOFILES, "")
fn=urlunquote(filepath)
fn = Path(settings.EXPOFILES,filepath)
if fn.is_dir():
return expofilesdir(request, Path(fn), Path(filepath))
if fn.is_file():
return HttpResponse(content=open(fn, "rb"),content_type=getmimetype(filepath)) # any file
else:
# not a file, so show parent directory - DANGER need to check this is limited to below expofiles
if Path(fn).parent == Path(settings.EXPOFILES).parent:
return expofilesdir(request, Path(settings.EXPOFILES), Path(filepath).parent)
else:
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 full Path() resolved including local machine /expofiles/
- filepath is a Path() and it does not have /expofiles/ in it
'''
#print(f' - expofilesdir {dirpath} settings.EXPOFILESREMOTE: {settings.EXPOFILESREMOTE}')
if filepath:
urlpath = 'expofiles' / Path(filepath)
else:
urlpath = Path('expofiles')
try:
for f in dirpath.iterdir():
pass
except FileNotFoundError:
#print(f' - expofilesdir error {dirpath}')
return expofilesdir(request, dirpath.parent, filepath.parent)
fileitems = []
diritems = []
for f in dirpath.iterdir():
if f.is_dir():
diritems.append((urlpath / f.parts[-1], str(f.parts[-1])))
else:
# if f.parts[-1].lower() == 'index.htm' or f.parts[-1].lower() == 'index.html': # css cwd problem
# return HttpResponse(content=open(f, "rb"),content_type=getmimetype(filepath)) # any file
# return expofilessingle(request, str(Path(filepath / f.parts[-1])))
fileitems.append((Path(urlpath) / f.parts[-1], str(f.parts[-1]), getmimetype(f)))
return render(request, 'dirdisplay.html', { 'filepath': urlpath, 'fileitems':fileitems, 'diritems': diritems,'settings': settings })
def expowebpage(request, expowebpath, path):
'''Adds menus and serves an HTML page
'''
if not Path(expowebpath / path).is_file():
# Should not get here if the path has suffix "_edit"
print(f' - 404 error in expowebpage() {path}')
return render(request, 'pagenotfound.html', {'path': path}, status="404")
try:
with open(os.path.normpath(expowebpath / path), "r") as o:
html = o.read()
except:
try:
with open(os.path.normpath(expowebpath / path), "rb") as o:
html = str(o.read()).replace("<h1>","<h1>BAD NON-UTF-8 characters here - ")
except:
return HttpResponse(default_head + '<h3>UTF-8 Parsing Failure:<br>Page could not be parsed using UTF-8:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to replace dubious umlauts and &pound; symbols with correct HTML entities e.g. <em>&amp;pound;;</em>. </body' )
m = re.search(r'(.*)<\s*head([^>]*)>(.*)<\s*/head\s*>(.*)<\s*body([^>]*)>(.*)<\s*/body\s*>(.*)', html, re.DOTALL + re.IGNORECASE)
if m:
preheader, headerattrs, head, postheader, bodyattrs, body, postbody = m.groups()
else:
return HttpResponse(default_head + html + '<h3>HTML Parsing failure:<br>Page could not be parsed into header and body:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to be in the expected full HTML format </body' )
m = re.search(r"<title>(.*)</title>", head, re.DOTALL + re.IGNORECASE)
if m:
title, = m.groups()
else:
title = ""
m = re.search(r"^<meta([^>]*)noedit", head, re.DOTALL + re.IGNORECASE)
if m:
editable = False
else:
editable = os.access(Path(expowebpath / path), os.W_OK) # are file permissions writeable?
has_menu = False
menumatch = re.match(r'(.*)<div id="menu">', body, re.DOTALL + re.IGNORECASE)
if menumatch:
has_menu = True
menumatch = re.match(r'(.*)<ul id="links">', body, re.DOTALL + re.IGNORECASE)
if menumatch:
has_menu = True
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):
'''This is for special prefix paths /photos/ /site_media/, /static/ etc.
as defined in urls.py . If given a directory, gives a failure page.
'''
#print(" - XXXXX_ROOT: {} ...{}".format(doc_root, subpath))
if doc_root is not None:
filetobeopened = Path(doc_root, subpath)
if filetobeopened.is_dir():
return render(request, 'nodirlist.html', {'path': subpath})
try:
return HttpResponse(content=open(filetobeopened, "rb"), content_type=getmimetype(subpath))
except IOError:
return render(request, 'pagenotfound.html', {'path': subpath}, status="404")
else:
return render(request, 'pagenotfound.html', {'path': subpath}, status="404")
def expopage(request, path):
'''Either renders an HTML page from expoweb with all the menus,
or serves an unadorned binary file with mime type
'''
#print(" - EXPOPAGES delivering the file: '{}':{} as MIME type: {}".format(request.path, path,getmimetype(path)),flush=True)
if path.startswith("noinfo") and settings.PUBLIC_SITE and not request.user.is_authenticated:
return HttpResponseRedirect(urljoin(reverse("auth_login"),'?next={}'.format(request.path)))
if path.startswith("admin/"):
# don't even attempt to handle these sorts of mistakes
return HttpResponseRedirect("/admin/")
expowebpath = Path(settings.EXPOWEB)
if path == "":
return expowebpage(request, expowebpath, "index.htm")
if path.endswith(".htm") or path.endswith(".html"):
return expowebpage(request, expowebpath, path)
if Path(expowebpath / path ).is_dir():
for p in ["index.html", "index.htm"]:
if (expowebpath / path / p).is_file():
# This needs to reset the path to the new subdirectory
return HttpResponseRedirect('/'+str(Path(path) / p))
return render(request, 'pagenotfound.html', {'path': Path(path) / "index.html"}, status="404")
if path.endswith("/"):
# we already know it is not a directory.
# the final / may have been appended by middleware if there was no page without it
# do not redirect to a file path without the slash as we may get in a loop. Let the user fix it:
return render(request, 'dirnotfound.html', {'path': path, 'subpath': path[0:-1]})
# So it must be a file in /expoweb/ but not .htm or .html probably an image
filetobeopened = os.path.normpath(expowebpath / path)
try:
return HttpResponse(content=open(filetobeopened, "rb"), content_type=getmimetype(path))
except IOError:
return render(request, 'pagenotfound.html', {'path': path}, status="404")
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"
if path.lower().endswith(".js"): return "application/javascript"
if path.lower().endswith(".json"): return "application/javascript"
if path.lower().endswith(".ico"): return "image/vnd.microsoft.icon"
if path.lower().endswith(".png"): return "image/png"
if path.lower().endswith(".tif"): return "image/tif"
if path.lower().endswith(".gif"): return "image/gif"
if path.lower().endswith(".jpeg"): return "image/jpeg"
if path.lower().endswith(".jpg"): return "image/jpeg"
if path.lower().endswith("svg"): return "image/svg+xml"
if path.lower().endswith("xml"): return "application/xml" # we use "text/xhtml" for tunnel files
if path.lower().endswith(".pdf"): return "application/pdf"
if path.lower().endswith(".ps"): return "application/postscript"
if path.lower().endswith(".svx"): return "application/x-survex-svx"
if path.lower().endswith(".3d"): return "application/x-survex-3d"
if path.lower().endswith(".pos"): return "application/x-survex-pos"
if path.lower().endswith(".err"): return "application/x-survex-err"
if path.lower().endswith(".odt"): return "application/vnd.oasis.opendocument.text"
if path.lower().endswith(".ods"): return "application/vnd.oasis.opendocument.spreadsheet"
if path.lower().endswith(".docx"): return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
if path.lower().endswith(".xslx"): return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
if path.lower().endswith(".gz"): return "application/x-7z-compressed"
if path.lower().endswith(".7z"): return "application/x-7z-compressed"
if path.lower().endswith(".zip"): return "application/zip"
return ""
@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 HTML5 or 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")
html = o.read()
autogeneratedmatch = re.search(r"\<\!--\s*(.*?(Do not edit|It is auto-generated).*?)\s*--\>", html, re.DOTALL + re.IGNORECASE)
if autogeneratedmatch:
return HttpResponse(autogeneratedmatch.group(1))
m = re.search(r"(.*)<head([^>]*)>(.*)</head>(.*)<body([^>]*)>(.*)</body>(.*)", html, re.DOTALL + re.IGNORECASE)
if m:
filefound = True
preheader, headerargs, head, postheader, bodyargs, body, postbody = m.groups()
linksmatch = re.match(r'(.*)(<ul\s+id="links">.*)', body, re.DOTALL + re.IGNORECASE)
if linksmatch:
body, links = linksmatch.groups()
# if re.search(r"iso-8859-1", html):
# body = str(body, "iso-8859-1")
else:
return HttpResponse(default_head + html + '<h3>HTML Parsing failure:<br>Page could not be parsed into header and body:<br>failure detected in expowebpage in views.expo.py</h3> Please edit this <var>:expoweb:</var> page to be in the expected full HTML format .</body' )
except IOError:
print("### File not found ### ", filepath)
filefound = False
if request.method == 'POST': # If the form has been submitted...
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>" + pageform.cleaned_data["title"] + "</title>" + headmatch.group(2)
else:
head = "<title>" + pageform.cleaned_data["title"] + "</title>"
else:
head = "<title>" + pageform.cleaned_data["title"] + "</title>"
preheader = "<html>"
headerargs = ""
postheader = ""
bodyargs = ""
postbody = "</html>\n"
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)
cwd = filepath.parent
filename = filepath.name
git = settings.GIT
# GIT see also core/models/cave.py writetrogglefile()
# GIT see also core/views/uploads.py dwgupload()
try:
with open(filepath, "w") as f:
print(f'WRITING{cwd}---{filename} ')
# as the wsgi process www-data, we have group write-access but are not owner, so cannot chmod.
# os.chmod(filepath, 0o664) # set file permissions to rw-rw-r--
f.write(result)
except PermissionError:
message = f'CANNOT save this file.\nPERMISSIONS incorrectly set on server for this file {filename}. Ask a nerd to fix this.'
return render(request,'errors/generic.html', {'message': message})
try:
cp_add = subprocess.run([git, "add", filename], cwd=cwd, capture_output=True, text=True)
if cp_add.returncode != 0:
msgdata = 'Ask a nerd to fix this.\n\n' + cp_add.stderr + '\n\n' + cp_add.stdout + '\n\nreturn code: ' + str(cp_add.returncode)
message = f'CANNOT git on server for this file {filename}. Edits saved but not added to git.\n\n' + msgdata
return render(request,'errors/generic.html', {'message': message})
cp_commit = subprocess.run([git, "commit", "-m", f'Troggle online: Edit this page - {filename}'], cwd=cwd, capture_output=True, text=True)
# This produces return code = 1 if it commits OK, but when the repo still needs to be pushed to origin/expoweb
if cp_commit.returncode != 0 and cp_commit.stdout != 'nothing to commit, working tree clean':
msgdata = 'Ask a nerd to fix this.\n\n' + cp_commit.stderr + '\n\n' + cp_commit.stdout + '\n\nreturn code: ' + str(cp_commit.returncode)
message = f'Error code with git on server for this file {filename}. Edits saved, added to git, but NOT committed.\n\n' + msgdata
return render(request,'errors/generic.html', {'message': message})
except subprocess.SubprocessError:
message = f'CANNOT git on server for this file {filename}. Subprocess error. Edits not saved.\nAsk a nerd to fix this.'
return render(request,'errors/generic.html', {'message': message})
return HttpResponseRedirect(reverse('expopage', args=[path])) # Redirect after POST
else:
if filefound:
m = re.search(r"<title>(.*)</title>", head, re.DOTALL + re.IGNORECASE)
if m:
title, = m.groups()
else:
title = ""
pageform = ExpoPageForm({"html": body, "title": title})
else:
body = "### File not found ###\n" + str(filepath)
pageform = ExpoPageForm({"html": body, "title": "Missing"})
return render(request, 'editexpopage.html', {'path': path, 'form': pageform, })
class ExpoPageForm(forms.Form):
'''The form used by the editexpopage function
'''
title = forms.CharField(widget=forms.TextInput(attrs={'size':'60'}))
html = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":20}))

View File

@ -1,173 +1,173 @@
import datetime
import os.path
import re
from collections import OrderedDict
import django.db.models
from django.db.models import Min, Max
from django.shortcuts import render
from django.template import Context, loader
from django.template.defaultfilters import slugify
from django.utils import timezone
#from django.views.generic.list import ListView
from troggle.core.models.troggle import Expedition, Person, PersonExpedition, DataIssue
from troggle.core.models.caves import Cave, LogbookEntry, Entrance
from troggle.core.models.survex import SurvexBlock, SurvexStation
import troggle.settings as settings
'''Very simple report pages summarizing data about the whole set of expeditions and of
the status of data inconsistencies
'''
def pathsreport(request):
pathsdict = OrderedDict()
try:
pathsdict = {
# "BOGUS" : str( settings.BOGUS),
"JSLIB_URL" : str( settings.JSLIB_URL),
"JSLIB_ROOT" : str( settings.JSLIB_ROOT),
# "CSSLIB_URL" : str( settings.CSSLIB_URL),
"CAVEDESCRIPTIONS" : str( settings.CAVEDESCRIPTIONS),
"DIR_ROOT" : str( settings.DIR_ROOT),
"ENTRANCEDESCRIPTIONS" : str( settings.ENTRANCEDESCRIPTIONS),
"EXPOUSER_EMAIL" : str( settings.EXPOUSER_EMAIL),
"EXPOUSERPASS" : str("<redacted>"),
"EXPOUSER" : str( settings.EXPOUSER),
"EXPOWEB" : str( settings.EXPOWEB),
"EXPOWEB_URL" : str( settings.EXPOWEB_URL),
"FILES" : str( settings.FILES),
"LIBDIR" : str( settings.LIBDIR),
"LOGFILE" : str( settings.LOGFILE),
"LOGIN_REDIRECT_URL" : str( settings.LOGIN_REDIRECT_URL),
"MEDIA_ROOT" : str( settings.MEDIA_ROOT),
"MEDIA_URL" : str( settings.MEDIA_URL),
"PHOTOS_URL" : str( settings.PHOTOS_URL),
"PYTHON_PATH" : str( settings.PYTHON_PATH),
"REPOS_ROOT_PATH" : str( settings.REPOS_ROOT_PATH),
"ROOT_URLCONF" : str( settings.ROOT_URLCONF),
"STATIC_URL" : str( settings.STATIC_URL),
"SURVEX_DATA" : str( settings.SURVEX_DATA),
"SURVEY_SCANS" : str( settings.SURVEY_SCANS),
"SURVEYS" : str( settings.SURVEYS),
"SURVEYS_URL" : str( settings.SURVEYS_URL),
"SURVEXPORT" : str( settings.SURVEXPORT),
"DRAWINGS_DATA" : str( settings.DRAWINGS_DATA),
"URL_ROOT" : str( settings.URL_ROOT)
}
except:
pathsdict["! EXCEPTION !"] = "missing or exta string constant in troggle/settings"
pathstype = OrderedDict()
try:
pathstype = {
# "BOGUS" : type(settings.BOGUS),
"JSLIB_URL" : type(settings.JSLIB_URL),
"JSLIB_ROOT" : type( settings.JSLIB_ROOT),
# "CSSLIB_URL" : type(settings.CSSLIB_URL),
"CAVEDESCRIPTIONS" : type(settings.CAVEDESCRIPTIONS),
"DIR_ROOT" : type(settings.DIR_ROOT),
"ENTRANCEDESCRIPTIONS" : type(settings.ENTRANCEDESCRIPTIONS),
"EXPOUSER_EMAIL" : type(settings.EXPOUSER_EMAIL),
"EXPOUSERPASS" : type(settings.EXPOUSERPASS),
"EXPOUSER" : type(settings.EXPOUSER),
"EXPOWEB" : type(settings.EXPOWEB),
"EXPOWEB_URL" : type(settings.EXPOWEB_URL),
"FILES" : type(settings.FILES),
"LIBDIR" : type( settings.LIBDIR),
"LOGFILE" : type(settings.LOGFILE),
"LOGIN_REDIRECT_URL" : type(settings.LOGIN_REDIRECT_URL),
"MEDIA_ROOT" : type(settings.MEDIA_ROOT),
"MEDIA_URL" : type(settings.MEDIA_URL),
"PHOTOS_URL" : type(settings.PHOTOS_URL),
"PYTHON_PATH" : type(settings.PYTHON_PATH),
"REPOS_ROOT_PATH" : type(settings.REPOS_ROOT_PATH),
"ROOT_URLCONF" : type(settings.ROOT_URLCONF),
"STATIC_URL" : type(settings.STATIC_URL),
"SURVEX_DATA" : type(settings.SURVEX_DATA),
"SURVEY_SCANS" : type(settings.SURVEY_SCANS),
"SURVEYS" : type(settings.SURVEYS),
"SURVEYS_URL" : type(settings.SURVEYS_URL),
"SURVEXPORT" : type(settings.SURVEXPORT),
"DRAWINGS_DATA" : type(settings.DRAWINGS_DATA),
"URL_ROOT" : type(settings.URL_ROOT)
}
except:
pathstype["! EXCEPTION !"] = "missing or exta string constant in troggle/settings"
raise
# settings are unique by paths are not
ncodes = len(pathsdict)
bycodeslist = sorted(pathsdict.items()) # a list of tuples
bycodeslist2 = []
for k, p in bycodeslist:
bycodeslist2.append((k, p, str(pathstype[k])))
bypaths = sorted(pathsdict.values()) # a list
bypathslist = []
for p in bypaths:
for k in pathsdict.keys():
if pathsdict[k] == p:
bypathslist.append((p, k, str(pathstype[k])))
del pathsdict[k]
break
return render(request, 'pathsreport.html', {
"pathsdict":pathsdict,
"bycodeslist":bycodeslist2,
"bypathslist":bypathslist,
"ncodes":ncodes})
def stats(request):
statsDict={}
statsDict['expoCount'] = "{:,}".format(Expedition.objects.count())
statsDict['caveCount'] = "{:,}".format(Cave.objects.count())
statsDict['personCount'] = "{:,}".format(Person.objects.count())
statsDict['logbookEntryCount'] = "{:,}".format(LogbookEntry.objects.count())
legsbyexpo = [ ]
addupsurvexlength = 0
for expedition in Expedition.objects.all():
survexblocks = expedition.survexblock_set.all()
legsyear=0
survexleglength = 0.0
for survexblock in survexblocks:
survexleglength += survexblock.legslength
try:
legsyear += int(survexblock.legsall)
except:
pass
addupsurvexlength += survexleglength
legsbyexpo.append((expedition, {"nsurvexlegs": "{:,}".format(legsyear),
"survexleglength":"{:,.0f}".format(survexleglength)}))
legsbyexpo.reverse()
renderDict = {**statsDict, **{ "addupsurvexlength":addupsurvexlength/1000, "legsbyexpo":legsbyexpo }} # new syntax
return render(request,'statistics.html', renderDict)
def dataissues(request):
'''Each issue has a parser, a message and a url linking to the offending object after loading
'''
def myFunc(di):
return di.parser.lower() + di.message.lower()
dilist = list(DataIssue.objects.all())
dilist.sort(key = myFunc)
return render(request,'dataissues.html', {'didict': dilist})
def eastings(request):
'''report each Northing/Easting pair wherever recorded
'''
ents = []
entrances = Entrance.objects.all()
for e in entrances:
if e.easting or e.northing:
ents.append(e)
stations = SurvexStation.objects.all()
return render(request,'eastings.html', {'ents': ents, 'stations': stations})
import datetime
import os.path
import re
from collections import OrderedDict
import django.db.models
from django.db.models import Min, Max
from django.shortcuts import render
from django.template import Context, loader
from django.template.defaultfilters import slugify
from django.utils import timezone
#from django.views.generic.list import ListView
from troggle.core.models.troggle import Expedition, Person, PersonExpedition, DataIssue
from troggle.core.models.caves import Cave, LogbookEntry, Entrance
from troggle.core.models.survex import SurvexBlock, SurvexStation
import troggle.settings as settings
'''Very simple report pages summarizing data about the whole set of expeditions and of
the status of data inconsistencies
'''
def pathsreport(request):
pathsdict = OrderedDict()
try:
pathsdict = {
# "BOGUS" : str( settings.BOGUS),
"JSLIB_URL" : str( settings.JSLIB_URL),
"JSLIB_ROOT" : str( settings.JSLIB_ROOT),
# "CSSLIB_URL" : str( settings.CSSLIB_URL),
"CAVEDESCRIPTIONS" : str( settings.CAVEDESCRIPTIONS),
"DIR_ROOT" : str( settings.DIR_ROOT),
"ENTRANCEDESCRIPTIONS" : str( settings.ENTRANCEDESCRIPTIONS),
"EXPOUSER_EMAIL" : str( settings.EXPOUSER_EMAIL),
"EXPOUSERPASS" : str("<redacted>"),
"EXPOUSER" : str( settings.EXPOUSER),
"EXPOWEB" : str( settings.EXPOWEB),
"EXPOWEB_URL" : str( settings.EXPOWEB_URL),
"FILES" : str( settings.FILES),
"LIBDIR" : str( settings.LIBDIR),
"LOGFILE" : str( settings.LOGFILE),
"LOGIN_REDIRECT_URL" : str( settings.LOGIN_REDIRECT_URL),
"MEDIA_ROOT" : str( settings.MEDIA_ROOT),
"MEDIA_URL" : str( settings.MEDIA_URL),
"PHOTOS_URL" : str( settings.PHOTOS_URL),
"PYTHON_PATH" : str( settings.PYTHON_PATH),
"REPOS_ROOT_PATH" : str( settings.REPOS_ROOT_PATH),
"ROOT_URLCONF" : str( settings.ROOT_URLCONF),
"STATIC_URL" : str( settings.STATIC_URL),
"SURVEX_DATA" : str( settings.SURVEX_DATA),
"SURVEY_SCANS" : str( settings.SURVEY_SCANS),
"SURVEYS" : str( settings.SURVEYS),
"SURVEYS_URL" : str( settings.SURVEYS_URL),
"SURVEXPORT" : str( settings.SURVEXPORT),
"DRAWINGS_DATA" : str( settings.DRAWINGS_DATA),
"URL_ROOT" : str( settings.URL_ROOT)
}
except:
pathsdict["! EXCEPTION !"] = "missing or exta string constant in troggle/settings"
pathstype = OrderedDict()
try:
pathstype = {
# "BOGUS" : type(settings.BOGUS),
"JSLIB_URL" : type(settings.JSLIB_URL),
"JSLIB_ROOT" : type( settings.JSLIB_ROOT),
# "CSSLIB_URL" : type(settings.CSSLIB_URL),
"CAVEDESCRIPTIONS" : type(settings.CAVEDESCRIPTIONS),
"DIR_ROOT" : type(settings.DIR_ROOT),
"ENTRANCEDESCRIPTIONS" : type(settings.ENTRANCEDESCRIPTIONS),
"EXPOUSER_EMAIL" : type(settings.EXPOUSER_EMAIL),
"EXPOUSERPASS" : type(settings.EXPOUSERPASS),
"EXPOUSER" : type(settings.EXPOUSER),
"EXPOWEB" : type(settings.EXPOWEB),
"EXPOWEB_URL" : type(settings.EXPOWEB_URL),
"FILES" : type(settings.FILES),
"LIBDIR" : type( settings.LIBDIR),
"LOGFILE" : type(settings.LOGFILE),
"LOGIN_REDIRECT_URL" : type(settings.LOGIN_REDIRECT_URL),
"MEDIA_ROOT" : type(settings.MEDIA_ROOT),
"MEDIA_URL" : type(settings.MEDIA_URL),
"PHOTOS_URL" : type(settings.PHOTOS_URL),
"PYTHON_PATH" : type(settings.PYTHON_PATH),
"REPOS_ROOT_PATH" : type(settings.REPOS_ROOT_PATH),
"ROOT_URLCONF" : type(settings.ROOT_URLCONF),
"STATIC_URL" : type(settings.STATIC_URL),
"SURVEX_DATA" : type(settings.SURVEX_DATA),
"SURVEY_SCANS" : type(settings.SURVEY_SCANS),
"SURVEYS" : type(settings.SURVEYS),
"SURVEYS_URL" : type(settings.SURVEYS_URL),
"SURVEXPORT" : type(settings.SURVEXPORT),
"DRAWINGS_DATA" : type(settings.DRAWINGS_DATA),
"URL_ROOT" : type(settings.URL_ROOT)
}
except:
pathstype["! EXCEPTION !"] = "missing or exta string constant in troggle/settings"
raise
# settings are unique by paths are not
ncodes = len(pathsdict)
bycodeslist = sorted(pathsdict.items()) # a list of tuples
bycodeslist2 = []
for k, p in bycodeslist:
bycodeslist2.append((k, p, str(pathstype[k])))
bypaths = sorted(pathsdict.values()) # a list
bypathslist = []
for p in bypaths:
for k in pathsdict.keys():
if pathsdict[k] == p:
bypathslist.append((p, k, str(pathstype[k])))
del pathsdict[k]
break
return render(request, 'pathsreport.html', {
"pathsdict":pathsdict,
"bycodeslist":bycodeslist2,
"bypathslist":bypathslist,
"ncodes":ncodes})
def stats(request):
statsDict={}
statsDict['expoCount'] = "{:,}".format(Expedition.objects.count())
statsDict['caveCount'] = "{:,}".format(Cave.objects.count())
statsDict['personCount'] = "{:,}".format(Person.objects.count())
statsDict['logbookEntryCount'] = "{:,}".format(LogbookEntry.objects.count())
legsbyexpo = [ ]
addupsurvexlength = 0
for expedition in Expedition.objects.all():
survexblocks = expedition.survexblock_set.all()
legsyear=0
survexleglength = 0.0
for survexblock in survexblocks:
survexleglength += survexblock.legslength
try:
legsyear += int(survexblock.legsall)
except:
pass
addupsurvexlength += survexleglength
legsbyexpo.append((expedition, {"nsurvexlegs": "{:,}".format(legsyear),
"survexleglength":"{:,.0f}".format(survexleglength)}))
legsbyexpo.reverse()
renderDict = {**statsDict, **{ "addupsurvexlength":addupsurvexlength/1000, "legsbyexpo":legsbyexpo }} # new syntax
return render(request,'statistics.html', renderDict)
def dataissues(request):
'''Each issue has a parser, a message and a url linking to the offending object after loading
'''
def myFunc(di):
return di.parser.lower() + di.message.lower()
dilist = list(DataIssue.objects.all())
dilist.sort(key = myFunc)
return render(request,'dataissues.html', {'didict': dilist})
def eastings(request):
'''report each Northing/Easting pair wherever recorded
'''
ents = []
entrances = Entrance.objects.all()
for e in entrances:
if e.easting or e.northing:
ents.append(e)
stations = SurvexStation.objects.all()
return render(request,'eastings.html', {'ents': ents, 'stations': stations})

View File

@ -1,164 +1,164 @@
From: <Saved by Blink>
Snapshot-Content-Location: https://aardgoose.github.io/CaveView.js/installation.html
Subject: CaveView installation
Date: Sun, 24 Oct 2021 15:46:19 -0000
MIME-Version: 1.0
Content-Type: multipart/related;
type="text/html";
boundary="----MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----"
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----
Content-Type: text/html
Content-ID: <frame-F25506009C17180F1D15EDA63865EFC8@mhtml.blink>
Content-Transfer-Encoding: quoted-printable
Content-Location: https://aardgoose.github.io/CaveView.js/installation.html
<!DOCTYPE html><html xmlns=3D"http://www.w3.org/1999/xhtml" xml:lang=3D"en-=
gb" lang=3D"en-gb" dir=3D"ltr"><head><meta http-equiv=3D"Content-Type" cont=
ent=3D"text/html; charset=3DUTF-8">
<title>CaveView installation</title>
=09
<link type=3D"text/css" href=3D"https://aardgoose.github.io/CaveView.js/do=
c.css" rel=3D"stylesheet">
</head>
<body>
<h1>Cave View - installation instructions - V2 API</h1>
<h2>Install CaveView files</h2>
<p>A zip archive containing the required files for the latest release are =
<a href=3D"https://github.com/aardgoose/CaveView.js/releases">here</a>.</p>
<p>There is no need to build the JavaScript application from source files =
unless you would like to modify the application.</p>
<p>Unzip the archive in a convenient location on your web server </p>
<h2>Using in a web page</h2>
<p>CaveView requires two files to be included in the page. Replace the plac=
eholder %path% with the path to the unzipped files on the server in the fol=
lowing examples.</p><p>
</p><h3>caveview.css</h3>
<p>The side panel interface and on screen indicators are styled with cavevi=
ew.css. Include this in the <strong>head</strong> section of the page.</p>
<pre> &lt;link type=3D"text/css" href=3D"%path%/CaveView/css/caveview.css" =
rel=3D"stylesheet" /&gt;</pre>
<h3>CaveView.js</h3>
<p>The CaveView application is provided in one javascript file. Include thi=
s in the <strong>body</strong> section of the page. <strong>NOTE: The filen=
ame has changed for the V2 API.</strong></p>
<pre> &lt;script type=3D"text/javascript" src=3D"%path%/CaveView/js/CaveVie=
w2.js" &gt;&lt;/script&gt;</pre>
<h3>Add a container element</h3>
<p>Add an empty block element to the page, with a suitable <strong>id</stro=
ng> attribute. This contains the application, and be sized as required. For=
example a <strong>div</strong> element:</p>
<pre>&lt;div id=3D'% element-id %' &gt;&lt;/div&gt;</pre>
<h3>Create a script to run the application</h3>
<p>The application can be loaded using javascript, typically using an load =
event handler. Replace the placeholder %...% elements with values appropria=
te for your site.</p>
<pre>&lt;script type=3D"text/javascript" &gt;
function onLoad () {
// display the user interface - and a blank canvas
// the configuration object specifies the location of CaveView, surveys a=
nd terrain files
var viewer =3D new CV2.CaveViewer( '% element-id %', {
home: '% location of the unzipped CaveView directory on the survey %',
surveyDirectory: '% location of the survey files on the server %',
terrainDirectory: '% location of the terrain files on the server %'
} );
// if using the full user interface (UI)
var ui =3D new CV2.CaveViewUI( viewer );
// load a single survey to display
ui.loadCave( '% survey filename %' );
// or without the user interface
viewer.loadCave( '% survey filename %' );
}
&lt;/script&gt;
</pre>
<p>alternatively provide a list of surveys to display, by replacing the loa=
dCave() function call with loadCaveList() :</p>
<pre> ui.loadCaveList( [ '% survey filename 1 %', '% survey filename 2 %', =
'% survey filename 3 %' ] );
</pre>
<p>This can be automatically executed on page load by including a page load=
handler in the page <strong>body</strong> tag:</p>
<pre>&lt;body onload=3D"onload();" &gt;</pre>
<p>CaveView should now display when the page is viewed.</p>
<p>The example files included demonstrate using multiple viewers on a page,
changing the default view settings and altering the appearance of the viewe=
r.</p>
<h3>Removal</h3>
<p>To remove the viewer from a page and reclaim memory used promptly:</p>
<pre> ui.dispose();
ui =3D null;
viewer =3D null;
</pre>
<p>Or if not using the UI, just the viewer:</p>
<pre> viewer.dispose();
viewer =3D null;
</pre>
</body></html>
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----
Content-Type: text/css
Content-Transfer-Encoding: quoted-printable
Content-Location: https://aardgoose.github.io/CaveView.js/doc.css
@charset "utf-8";
h1, h2 { margin: 0px 16px 1em; }
h3, h4, h5, p, pre, dl { margin-left: 32px; margin-right: 32px; }
h2 { border-bottom: 2px solid gray; }
h3 { color: navy; }
h4 { margin-top: 2em; background-color: rgb(238, 238, 238); padding: 3px 2p=
x 1px; }
* { font-family: sans-serif; }
pre { font-family: monospace; font-weight: bold; background-color: rgb(238,=
238, 238); padding: 8px; }
dd > span { display: inline-block; padding: 0px 2px; margin-right: 5px; bac=
kground: rgb(238, 238, 238); }
dd > span::before { content: "["; }
dd > span::after { content: "]"; }
dt { margin-bottom: 0.5em; font-weight: bold; color: rgb(102, 102, 102); pa=
dding-left: 16px; }
dd { margin-bottom: 1em; }
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ------
From: <Saved by Blink>
Snapshot-Content-Location: https://aardgoose.github.io/CaveView.js/installation.html
Subject: CaveView installation
Date: Sun, 24 Oct 2021 15:46:19 -0000
MIME-Version: 1.0
Content-Type: multipart/related;
type="text/html";
boundary="----MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----"
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----
Content-Type: text/html
Content-ID: <frame-F25506009C17180F1D15EDA63865EFC8@mhtml.blink>
Content-Transfer-Encoding: quoted-printable
Content-Location: https://aardgoose.github.io/CaveView.js/installation.html
<!DOCTYPE html><html xmlns=3D"http://www.w3.org/1999/xhtml" xml:lang=3D"en-=
gb" lang=3D"en-gb" dir=3D"ltr"><head><meta http-equiv=3D"Content-Type" cont=
ent=3D"text/html; charset=3DUTF-8">
<title>CaveView installation</title>
=09
<link type=3D"text/css" href=3D"https://aardgoose.github.io/CaveView.js/do=
c.css" rel=3D"stylesheet">
</head>
<body>
<h1>Cave View - installation instructions - V2 API</h1>
<h2>Install CaveView files</h2>
<p>A zip archive containing the required files for the latest release are =
<a href=3D"https://github.com/aardgoose/CaveView.js/releases">here</a>.</p>
<p>There is no need to build the JavaScript application from source files =
unless you would like to modify the application.</p>
<p>Unzip the archive in a convenient location on your web server </p>
<h2>Using in a web page</h2>
<p>CaveView requires two files to be included in the page. Replace the plac=
eholder %path% with the path to the unzipped files on the server in the fol=
lowing examples.</p><p>
</p><h3>caveview.css</h3>
<p>The side panel interface and on screen indicators are styled with cavevi=
ew.css. Include this in the <strong>head</strong> section of the page.</p>
<pre> &lt;link type=3D"text/css" href=3D"%path%/CaveView/css/caveview.css" =
rel=3D"stylesheet" /&gt;</pre>
<h3>CaveView.js</h3>
<p>The CaveView application is provided in one javascript file. Include thi=
s in the <strong>body</strong> section of the page. <strong>NOTE: The filen=
ame has changed for the V2 API.</strong></p>
<pre> &lt;script type=3D"text/javascript" src=3D"%path%/CaveView/js/CaveVie=
w2.js" &gt;&lt;/script&gt;</pre>
<h3>Add a container element</h3>
<p>Add an empty block element to the page, with a suitable <strong>id</stro=
ng> attribute. This contains the application, and be sized as required. For=
example a <strong>div</strong> element:</p>
<pre>&lt;div id=3D'% element-id %' &gt;&lt;/div&gt;</pre>
<h3>Create a script to run the application</h3>
<p>The application can be loaded using javascript, typically using an load =
event handler. Replace the placeholder %...% elements with values appropria=
te for your site.</p>
<pre>&lt;script type=3D"text/javascript" &gt;
function onLoad () {
// display the user interface - and a blank canvas
// the configuration object specifies the location of CaveView, surveys a=
nd terrain files
var viewer =3D new CV2.CaveViewer( '% element-id %', {
home: '% location of the unzipped CaveView directory on the survey %',
surveyDirectory: '% location of the survey files on the server %',
terrainDirectory: '% location of the terrain files on the server %'
} );
// if using the full user interface (UI)
var ui =3D new CV2.CaveViewUI( viewer );
// load a single survey to display
ui.loadCave( '% survey filename %' );
// or without the user interface
viewer.loadCave( '% survey filename %' );
}
&lt;/script&gt;
</pre>
<p>alternatively provide a list of surveys to display, by replacing the loa=
dCave() function call with loadCaveList() :</p>
<pre> ui.loadCaveList( [ '% survey filename 1 %', '% survey filename 2 %', =
'% survey filename 3 %' ] );
</pre>
<p>This can be automatically executed on page load by including a page load=
handler in the page <strong>body</strong> tag:</p>
<pre>&lt;body onload=3D"onload();" &gt;</pre>
<p>CaveView should now display when the page is viewed.</p>
<p>The example files included demonstrate using multiple viewers on a page,
changing the default view settings and altering the appearance of the viewe=
r.</p>
<h3>Removal</h3>
<p>To remove the viewer from a page and reclaim memory used promptly:</p>
<pre> ui.dispose();
ui =3D null;
viewer =3D null;
</pre>
<p>Or if not using the UI, just the viewer:</p>
<pre> viewer.dispose();
viewer =3D null;
</pre>
</body></html>
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ----
Content-Type: text/css
Content-Transfer-Encoding: quoted-printable
Content-Location: https://aardgoose.github.io/CaveView.js/doc.css
@charset "utf-8";
h1, h2 { margin: 0px 16px 1em; }
h3, h4, h5, p, pre, dl { margin-left: 32px; margin-right: 32px; }
h2 { border-bottom: 2px solid gray; }
h3 { color: navy; }
h4 { margin-top: 2em; background-color: rgb(238, 238, 238); padding: 3px 2p=
x 1px; }
* { font-family: sans-serif; }
pre { font-family: monospace; font-weight: bold; background-color: rgb(238,=
238, 238); padding: 8px; }
dd > span { display: inline-block; padding: 0px 2px; margin-right: 5px; bac=
kground: rgb(238, 238, 238); }
dd > span::before { content: "["; }
dd > span::after { content: "]"; }
dt { margin-bottom: 0.5em; font-weight: bold; color: rgb(102, 102, 102); pa=
dding-left: 16px; }
dd { margin-bottom: 1em; }
------MultipartBoundary--eCFBXfFmJiPIUIDdJDuaiHRJfZ59xqA99QxmIcmsBZ------

View File

@ -1,2 +1,2 @@
[InternetShortcut]
URL=https://github.com/aardgoose/CaveView.js/releases
[InternetShortcut]
URL=https://github.com/aardgoose/CaveView.js/releases

View File

@ -1,2 +1,2 @@
[InternetShortcut]
URL=https://aardgoose.github.io/CaveView.js/
[InternetShortcut]
URL=https://aardgoose.github.io/CaveView.js/