mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2024-12-01 06:11:51 +00:00
Allowed user to select/upload images when editing. When uploaded thumbnails and description pages are automatically created. Git commiting can now handle multiple files at once.
This commit is contained in:
parent
b3d9e81499
commit
20583b04c0
111
core/views/editor_helpers.py
Normal file
111
core/views/editor_helpers.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect, Http404, JsonResponse
|
||||||
|
|
||||||
|
from django.urls import reverse, resolve
|
||||||
|
from django.template import Context, loader
|
||||||
|
import re, io
|
||||||
|
from PIL import Image
|
||||||
|
from pathlib import Path
|
||||||
|
import django.forms as forms
|
||||||
|
import troggle.settings as settings
|
||||||
|
|
||||||
|
from troggle.lib import version_control
|
||||||
|
|
||||||
|
MAX_IMAGE_WIDTH = 1000
|
||||||
|
MAX_IMAGE_HEIGTH = 800
|
||||||
|
|
||||||
|
THUMBNAIL_WIDTH = 200
|
||||||
|
THUMBNAIL_HEIGTH = 200
|
||||||
|
|
||||||
|
def image_selector(request, path):
|
||||||
|
'''Returns available images'''
|
||||||
|
directory = path.rsplit('/', 1)[0]
|
||||||
|
thumbnailspath = Path(settings.EXPOWEB) / directory / "t"
|
||||||
|
thumbnails = []
|
||||||
|
for f in thumbnailspath.iterdir():
|
||||||
|
if f.is_file():
|
||||||
|
thumbnail_url = reverse('expopage', args=["%s/t/%s" % (directory, f.name)])
|
||||||
|
name_base = f.name.rsplit('.', 1)[0]
|
||||||
|
page_path_base = Path(settings.EXPOWEB) / directory / "l"
|
||||||
|
if ((page_path_base / ("%s.htm" % name_base)).is_file()):
|
||||||
|
page_url = reverse('expopage', args=["%s/l/%s.htm" % (directory, name_base)])
|
||||||
|
else:
|
||||||
|
page_url = reverse('expopage', args=["%s/l/%s.html" % (directory, name_base)])
|
||||||
|
|
||||||
|
thumbnails.append({"thumbnail_url": thumbnail_url, "page_url": page_url})
|
||||||
|
|
||||||
|
return render(request, 'image_selector.html', {'thumbnails': thumbnails})
|
||||||
|
|
||||||
|
def new_image_form(request, path):
|
||||||
|
'''Manages a form to upload new images'''
|
||||||
|
directory = path.rsplit('/', 1)[0]
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = NewWebImageForm(request.POST, request.FILES, directory = directory)
|
||||||
|
if form.is_valid():
|
||||||
|
f = request.FILES["file_"]
|
||||||
|
binary_data = io.BytesIO()
|
||||||
|
for chunk in f.chunks():
|
||||||
|
binary_data.write(chunk)
|
||||||
|
i = Image.open(binary_data)
|
||||||
|
width, height = i.size
|
||||||
|
if width > MAX_IMAGE_WIDTH or height > MAX_IMAGE_HEIGTH:
|
||||||
|
scale = max(width / MAX_IMAGE_WIDTH, height / MAX_IMAGE_HEIGTH)
|
||||||
|
i = i.resize((int(width / scale), int(height / scale)), Image.ANTIALIAS)
|
||||||
|
tscale = max(width / THUMBNAIL_WIDTH, height / THUMBNAIL_HEIGTH)
|
||||||
|
thumbnail = i.resize((int(width / tscale), int(height / tscale)), Image.ANTIALIAS)
|
||||||
|
ib = io.BytesIO()
|
||||||
|
i.save(ib, format="png")
|
||||||
|
tb = io.BytesIO()
|
||||||
|
thumbnail.save(tb, format="png")
|
||||||
|
image_rel_path, thumb_rel_path, desc_rel_path = form.get_rel_paths()
|
||||||
|
image_page_template = loader.get_template('image_page_template.html')
|
||||||
|
image_page = image_page_template.render({'header': form.cleaned_data["header"], 'description': form.cleaned_data["description"],
|
||||||
|
'photographer': form.cleaned_data["photographer"], 'year': form.cleaned_data["year"],
|
||||||
|
'filepath': f'/{image_rel_path}'
|
||||||
|
})
|
||||||
|
image_path, thumb_path, desc_path = form.get_full_paths()
|
||||||
|
try:
|
||||||
|
change_message = form.cleaned_data["change_message"]
|
||||||
|
version_control.write_and_commit([(desc_path, image_page, "utf-8"),
|
||||||
|
(image_path, ib.getbuffer(), False),
|
||||||
|
(thumb_path, tb.getbuffer(), False)],
|
||||||
|
f'{change_message} - online adding of an image')
|
||||||
|
except version_control.WriteAndCommitError as e:
|
||||||
|
return JsonResponse({"error": e.message})
|
||||||
|
linked_image_template = loader.get_template('linked_image_template.html')
|
||||||
|
html_snippet = linked_image_template.render({'thumbnail_url': f'/{thumb_rel_path}', 'page_url': f'/{desc_rel_path}'}, request)
|
||||||
|
return JsonResponse({"html": html_snippet})
|
||||||
|
else:
|
||||||
|
form = NewWebImageForm(directory = directory)
|
||||||
|
template = loader.get_template('new_image_form.html')
|
||||||
|
htmlform = template.render({'form': form, 'path': path}, request)
|
||||||
|
return JsonResponse({"form": htmlform})
|
||||||
|
|
||||||
|
class NewWebImageForm(forms.Form):
|
||||||
|
'''The form used by the editexpopage function
|
||||||
|
'''
|
||||||
|
header = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Enter title (displayed as a header and in the tab)"}))
|
||||||
|
file_ = forms.FileField()
|
||||||
|
description = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":20, 'placeholder': "Describe the photo (using HTML)"}))
|
||||||
|
photographer = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Photographers name"}), required = False)
|
||||||
|
year = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Year photo was taken"}), required = False)
|
||||||
|
change_message = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":3, 'placeholder': "Descibe the change made (for git)"}))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.directory = Path(kwargs.pop('directory'))
|
||||||
|
super(forms.Form, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_rel_paths(self):
|
||||||
|
f = self.cleaned_data['file_']
|
||||||
|
return [self.directory / "i" / (f.name.rsplit('.', 1)[0] + ".png"),
|
||||||
|
self.directory / "t" / (f.name.rsplit('.', 1)[0] + ".png"),
|
||||||
|
self.directory / "l" / (f.name.rsplit('.', 1)[0] + ".html")]
|
||||||
|
|
||||||
|
def get_full_paths(self):
|
||||||
|
return [Path(settings.EXPOWEB) / x for x in self.get_rel_paths()]
|
||||||
|
|
||||||
|
def clean_file_(self):
|
||||||
|
for rel_path, full_path in zip(self.get_rel_paths(), self.get_full_paths()):
|
||||||
|
if full_path.exists():
|
||||||
|
raise forms.ValidationError("File already exists in %s" % rel_path)
|
||||||
|
return self.cleaned_data['file_']
|
@ -359,7 +359,7 @@ def editexpopage(request, path):
|
|||||||
if result != html: # Check if content changed
|
if result != html: # Check if content changed
|
||||||
try:
|
try:
|
||||||
change_message = pageform.cleaned_data["change_message"]
|
change_message = pageform.cleaned_data["change_message"]
|
||||||
version_control.write_and_commit(filepath, result, f'{change_message} - online edit of {path}')
|
version_control.write_and_commit([(filepath, result, "utf-8")], f'{change_message} - online edit of {path}')
|
||||||
except version_control.WriteAndCommitError as e:
|
except version_control.WriteAndCommitError as e:
|
||||||
return render(request,'errors/generic.html', {'message': e.message})
|
return render(request,'errors/generic.html', {'message': e.message})
|
||||||
|
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
import troggle.settings as settings
|
import troggle.settings as settings
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
def write_and_commit(filepath, content, message):
|
def write_and_commit(files, message):
|
||||||
"""Writes the content to the filepath and adds and commits the file to git. If this fails, a WriteAndCommitError is raised."""
|
"""Writes the content to the filepath and adds and commits the file to git. If this fails, a WriteAndCommitError is raised."""
|
||||||
|
git = settings.GIT
|
||||||
|
try:
|
||||||
|
for filepath, content, encoding in files:
|
||||||
cwd = filepath.parent
|
cwd = filepath.parent
|
||||||
filename = filepath.name
|
filename = filepath.name
|
||||||
git = settings.GIT
|
|
||||||
# GIT see also core/models/cave.py writetrogglefile()
|
# GIT see also core/models/cave.py writetrogglefile()
|
||||||
# GIT see also core/views/uploads.py dwgupload()
|
# GIT see also core/views/uploads.py dwgupload()
|
||||||
|
|
||||||
|
if encoding:
|
||||||
|
mode = "w"
|
||||||
|
kwargs = {"encoding": encoding}
|
||||||
|
else:
|
||||||
|
mode = "wb"
|
||||||
|
kwargs = {}
|
||||||
try:
|
try:
|
||||||
with open(filepath, "w", encoding="utf8") as f:
|
with open(filepath, mode, **kwargs) as f:
|
||||||
print(f'WRITING{cwd}---{filename} ')
|
print(f'WRITING{cwd}---{filename} ')
|
||||||
# as the wsgi process www-data, we have group write-access but are not owner, so cannot chmod.
|
# 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--
|
# os.chmod(filepath, 0o664) # set file permissions to rw-rw-r--
|
||||||
@ -18,7 +26,6 @@ def write_and_commit(filepath, content, message):
|
|||||||
except PermissionError:
|
except PermissionError:
|
||||||
raise WriteAndCommitError(f'CANNOT save this file.\nPERMISSIONS incorrectly set on server for this file {filename}. Ask a nerd to fix this.')
|
raise WriteAndCommitError(f'CANNOT save this file.\nPERMISSIONS incorrectly set on server for this file {filename}. Ask a nerd to fix this.')
|
||||||
|
|
||||||
try:
|
|
||||||
cp_add = subprocess.run([git, "add", filename], cwd=cwd, capture_output=True, text=True)
|
cp_add = subprocess.run([git, "add", filename], cwd=cwd, capture_output=True, text=True)
|
||||||
if cp_add.returncode != 0:
|
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)
|
msgdata = 'Ask a nerd to fix this.\n\n' + cp_add.stderr + '\n\n' + cp_add.stdout + '\n\nreturn code: ' + str(cp_add.returncode)
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
<!--<script src="{{ settings.TINY_MCE_MEDIA_URL }}tiny_mce.js" type="text/javascript"></script>-->
|
<!--<script src="{{ settings.TINY_MCE_MEDIA_URL }}tiny_mce.js" type="text/javascript"></script>-->
|
||||||
<!-- <script type="text/javascript"> tinyMCE.init({ mode : "textareas" }); </script>-->
|
<!-- <script type="text/javascript"> tinyMCE.init({ mode : "textareas" }); </script>-->
|
||||||
|
|
||||||
|
<script src="{{ settings.MEDIA_URL }}admin/js/vendor/jquery/jquery.js" type="text/javascript"></script>
|
||||||
|
|
||||||
<script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script>
|
<script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script>
|
||||||
<script src={{ settings.MEDIA_URL }}codemirror/xml.js></script>
|
<script src={{ settings.MEDIA_URL }}codemirror/xml.js></script>
|
||||||
<script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script>
|
<script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script>
|
||||||
@ -38,9 +40,82 @@
|
|||||||
height: 5%;
|
height: 5%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<style type=text/css>
|
||||||
|
html {
|
||||||
|
font-family: "Helvetica Neue", sans-serif;
|
||||||
|
width: 100%;
|
||||||
|
color: #666666;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-overlay {
|
||||||
|
/*Hides pop-up when there is no "active" class*/
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 3px solid #666666;
|
||||||
|
width: 90%;
|
||||||
|
height: 80%;
|
||||||
|
overflow: scroll;
|
||||||
|
left: 5%;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-overlay.active {
|
||||||
|
/*displays pop-up when "active" class is present*/
|
||||||
|
visibility: visible;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
/*Hides pop-up content when there is no "active" class */
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content.active {
|
||||||
|
/*Shows pop-up content when "active" class is present */
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 30px;
|
||||||
|
margin: .20rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #666666;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
border: 1px solid #666666;
|
||||||
|
background: #666666;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h1>Edit {{ path }}</h1>
|
<h1>Edit {{ path }}</h1>
|
||||||
|
<!--Creates the add image popup-->
|
||||||
|
<div class="add-image-popup popup-overlay">
|
||||||
|
<div class="add-image-popup popup-content">
|
||||||
|
<h2>Select Image</h2>
|
||||||
|
<p id="image_popup_content"> Loading ...</p>
|
||||||
|
<button onclick="new_image_popup()">Upload Image</button>
|
||||||
|
<button class="close" onclick="$('.add-image-popup').removeClass('active');">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--Creates the new image popup-->
|
||||||
|
<div class="new-image-popup popup-overlay">
|
||||||
|
<div class="new-image-popup popup-content">
|
||||||
|
<h2>New Image</h2>
|
||||||
|
<p id="new_image_popup_content"> Loading ...</p>
|
||||||
|
<button class="close" onclick="$('.new-image-popup').removeClass('active');">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
{{ form.non_field_errors }}
|
{{ form.non_field_errors }}
|
||||||
<div class="fieldWrapper">
|
<div class="fieldWrapper">
|
||||||
@ -62,6 +137,7 @@
|
|||||||
<button type="button" onclick="addTag('h4', '')">heading 4</button>
|
<button type="button" onclick="addTag('h4', '')">heading 4</button>
|
||||||
<button type="button" onclick="addTag('a', 'href=""')">hyperlink</button>
|
<button type="button" onclick="addTag('a', 'href=""')">hyperlink</button>
|
||||||
<button type="button" onclick="addTag('p', '')">paragraph</button>
|
<button type="button" onclick="addTag('p', '')">paragraph</button>
|
||||||
|
<button type="button" onclick="add_image_popup()">image</button>
|
||||||
<div class="fieldWrapper">
|
<div class="fieldWrapper">
|
||||||
{{ form.change_message.errors }}
|
{{ form.change_message.errors }}
|
||||||
<label for="{{ form.title.id_for_label }}">Git change message:</label>
|
<label for="{{ form.title.id_for_label }}">Git change message:</label>
|
||||||
@ -70,6 +146,60 @@
|
|||||||
{% include "menu.html" %}
|
{% include "menu.html" %}
|
||||||
<p><input type="submit" value="Submit" /></p>
|
<p><input type="submit" value="Submit" /></p>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
function add_image_popup() {
|
||||||
|
$('.add-image-popup').addClass('active');
|
||||||
|
$('#image_popup_content').load("{% url 'image_selector' path %}", function() {
|
||||||
|
$('.thumbnail').click(function(){
|
||||||
|
$(".add-image-popup").removeClass("active");
|
||||||
|
addStr($( this ).attr("data-html"))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function new_image_popup() {
|
||||||
|
$('.add-image-popup').removeClass('active');
|
||||||
|
$('.new-image-popup').addClass('active');
|
||||||
|
$.ajax({
|
||||||
|
type : "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "{% url 'new_image_form' path %}",
|
||||||
|
success: function(data){handle_new_image(data)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_new_image(data) {
|
||||||
|
if (data.hasOwnProperty('form')) {
|
||||||
|
$('#new_image_popup_content').html(data.form);
|
||||||
|
$('#new_image_form').on('submit', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
data = $('#new_image_form').serialize();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type : "POST",
|
||||||
|
dataType: "json",
|
||||||
|
url: "{% url 'new_image_form' path %}",
|
||||||
|
data: new FormData($('#new_image_form')[0]),
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(data){
|
||||||
|
handle_new_image(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (data.hasOwnProperty('html')) {
|
||||||
|
$('.new-image-popup').removeClass('active');
|
||||||
|
addStr(data.html);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert(data.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
var delay;
|
var delay;
|
||||||
// Initialize CodeMirror editor with a nice html5 canvas demo.
|
// Initialize CodeMirror editor with a nice html5 canvas demo.
|
||||||
@ -103,5 +233,12 @@
|
|||||||
editor.focus();
|
editor.focus();
|
||||||
editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length });
|
editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addStr(x){
|
||||||
|
var to = editor.getCursor(false);
|
||||||
|
editor.replaceRange(x, to);
|
||||||
|
editor.focus();
|
||||||
|
editor.setCursor({line: to.line , ch : to.ch + x.length });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
24
templates/image_page_template.html
Normal file
24
templates/image_page_template.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
|
||||||
|
<title>
|
||||||
|
{{ header }}
|
||||||
|
</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../../css/main2.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<H1>{{ header }}</H1>
|
||||||
|
<div class="centre"><img alt="" src="{{ filepath }}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>{{ description }}</p>
|
||||||
|
|
||||||
|
{% if photographer %}
|
||||||
|
<p class="caption">Photo © {{ photographer }}{% if year %}, {{ year }}{% endif %}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
</body>
|
||||||
|
</html>
|
3
templates/image_selector.html
Normal file
3
templates/image_selector.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% for thumbnail in thumbnails %}
|
||||||
|
<img class = "thumbnail" src = "{{ thumbnail.thumbnail_url }}" data-html = "{% include 'linked_image_template.html' with thumbnail_url=thumbnail.thumbnail_url page_url=thumbnail.page_url %}"/>
|
||||||
|
{% endfor %}
|
1
templates/linked_image_template.html
Normal file
1
templates/linked_image_template.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<a href='{{ page_url }}'><img src='{{ thumbnail_url }}' /></a>
|
5
templates/new_image_form.html
Normal file
5
templates/new_image_form.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<form id="new_image_form" action="{% url 'new_image_form' path %}" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
6
urls.py
6
urls.py
@ -23,6 +23,7 @@ from troggle.core.views.statistics import pathsreport, stats, dataissues
|
|||||||
from troggle.core.views.expo import expofiles_redirect, expofilessingle, expopage, editexpopage, mediapage, map, mapfile
|
from troggle.core.views.expo import expofiles_redirect, expofilessingle, expopage, editexpopage, mediapage, map, mapfile
|
||||||
from troggle.core.views.survex import survexcaveslist, survexcavesingle, svx
|
from troggle.core.views.survex import survexcaveslist, survexcavesingle, svx
|
||||||
from troggle.core.views.auth import expologin, expologout
|
from troggle.core.views.auth import expologin, expologout
|
||||||
|
from troggle.core.views.editor_helpers import image_selector, new_image_form
|
||||||
"""This sets the actualurlpatterns[] and urlpatterns[] lists which django uses
|
"""This sets the actualurlpatterns[] and urlpatterns[] lists which django uses
|
||||||
to resolve urls - in both directions as these are declarative.
|
to resolve urls - in both directions as these are declarative.
|
||||||
|
|
||||||
@ -191,6 +192,11 @@ trogglepatterns = [
|
|||||||
re_path(r'^map/map.html', map, name="map"), # Redirects to OpenStreetMap JavaScript
|
re_path(r'^map/map.html', map, name="map"), # Redirects to OpenStreetMap JavaScript
|
||||||
re_path(r'^map/(?P<path>.*)$', mapfile, name="mapfile"), # css, js, gpx
|
re_path(r'^map/(?P<path>.*)$', mapfile, name="mapfile"), # css, js, gpx
|
||||||
|
|
||||||
|
# Helpers to edit HTML
|
||||||
|
re_path(r'^image_selector/(?P<path>.*)', image_selector, name = 'image_selector'),
|
||||||
|
re_path(r'^new_image_form/(?P<path>.*)', new_image_form, name = 'new_image_form'),
|
||||||
|
|
||||||
|
|
||||||
# Final catchall which also serves expoweb handbook pages and images
|
# Final catchall which also serves expoweb handbook pages and images
|
||||||
re_path(r'^(.*)$', expopage, name="expopage"), # CATCHALL assumed relative to EXPOWEB
|
re_path(r'^(.*)$', expopage, name="expopage"), # CATCHALL assumed relative to EXPOWEB
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user