2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2026-03-31 21:44:00 +01:00

lgoing/cookie interaction betetr

This commit is contained in:
2025-02-13 15:10:12 +00:00
parent 3a3e5765f9
commit 3fb99eb4be
4 changed files with 131 additions and 84 deletions

View File

@@ -168,25 +168,6 @@ def current_expo():
else: else:
return settings.EPOCH.year # this is 1970 return settings.EPOCH.year # this is 1970
def is_identified_user(user):
if user.is_anonymous:
return False
if user.username in ["expo", "expoadmin"]:
return False
return True
def get_git_string(user):
if not is_identified_user(user):
return None
else:
people = Person.objects.filter(user=user)
if len(people) != 1:
# someone like "fluffy-bunny" not associated with a Person
return None
person = people[0]
return f"{person.fullname} <{user.email}>"
def parse_aliases(aliasfile): def parse_aliases(aliasfile):
"""Reads a long text string containing pairs of strings: """Reads a long text string containing pairs of strings:
(alias, target) (alias, target)
@@ -233,14 +214,40 @@ def parse_aliases(aliasfile):
return [(None, None)], "Fail on file reading" return [(None, None)], "Fail on file reading"
return aliases, report return aliases, report
def get_editor(request):
current_user = request.user
if is_identified_user(current_user):
return get_git_string(current_user)
else:
return get_cookie(request)
def is_identified_user(user):
if user.is_anonymous:
return False
if user.username in ["expo", "expoadmin"]:
return False
return True
def get_git_string(user):
if not is_identified_user(user):
return None
else:
people = Person.objects.filter(user=user)
if len(people) != 1:
# someone like "fluffy-bunny" not associated with a Person
return None
person = people[0]
return f"{person.fullname} <{user.email}>"
def get_cookie(request): def get_cookie(request):
"""The initial idea of having a default turned out to be a bad idea as people just ignore the field. """The initial idea of having a default turned out to be a bad idea as people just ignore the field.
if the default value is blank, then the form validation code makes the user type something in, if the default value is blank, then the form validation code makes the user type something in.
so having a blank is best. So having a blank is best if the cookie is to be displayed as the first value seen on a form.
But if the cookie is to be stored, then "Unset" may be better.
""" """
# NO_COOKIE_DEFAULT = 'Unset Cookie <hohlenforscher@potatohut.expo>' # NO_COOKIE_DEFAULT = 'Unset Cookie <hohlenforscher@potatohut.expo>'
# print(f"-- Getting cookie...") # print(f"-- Getting cookie...")
editor_id = request.COOKIES.get('editor_id', "") # if no cookie, then default string "" editor_id = request.COOKIES.get('editor_id', "")
editor = git_string(editor_id) # belt and braces, should have been validity checked on saving already editor = git_string(editor_id) # belt and braces, should have been validity checked on saving already
# print(f"-- Cookie to be used: {editor=}") # print(f"-- Cookie to be used: {editor=}")
return editor return editor
@@ -268,7 +275,6 @@ def git_string(author_string):
print(f"++ Not git-compatible author string '{author_string}', replacing as '{editor}'") print(f"++ Not git-compatible author string '{author_string}', replacing as '{editor}'")
return editor return editor
def git_add(filename, cwd, commands=[]): def git_add(filename, cwd, commands=[]):
"""Add a file to the list of Staged files ready for a later git commit """Add a file to the list of Staged files ready for a later git commit
""" """

View File

@@ -17,8 +17,11 @@ from PIL import Image
import troggle.settings as settings import troggle.settings as settings
from troggle.core.utils import ( COOKIE_MAX_AGE, from troggle.core.utils import ( COOKIE_MAX_AGE,
WriteAndCommitError, get_cookie, git_string, write_binary_file, WriteAndCommitError, get_editor,
write_and_commit, current_expo, random_slug, ensure_dir_exists git_string,
write_binary_file, write_and_commit,
current_expo, random_slug, ensure_dir_exists,
is_identified_user
) )
from .auth import login_required_if_public from .auth import login_required_if_public
@@ -115,47 +118,71 @@ def dms2dd(degrees, minutes, seconds, direction):
def extract_gps(dict): def extract_gps(dict):
"""Produce a set of annotations to add to an image description """Produce a set of annotations to add to an image description
The problem is that at any time one or more of the exif data points might
be missing from a particular photo, even GPSVersionID, so we need a lot
of data existence checking or it will crash.
""" """
def is_present(gpsifd):
item = getattr(piexif.GPSIFD, gpsifd)
if item in dict:
print(f" {gpsifd} = {item}")
return dict[item]
return None
def extract(gpsifd):
if item:=is_present(gpsifd): # walrus
n, d = item
return n/d
return None
def rational(tup): def rational(tup):
nom, denom = tup nom, denom = tup
return nom/denom return nom/denom
def extract(gpsifd):
print(piexif.GPSIFD)
n, d = dict[getattr(piexif.GPSIFD, gpsifd)]
return n/d
compass_points = ["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"] compass_points = ["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"]
bearing = extract("GPSImgDirection") if bearing := extract("GPSImgDirection"):
compass_lookup = round(bearing / 45) compass_lookup = round(bearing / 45)
nsew = compass_points[compass_lookup] nsew = compass_points[compass_lookup]
if dict[piexif.GPSIFD.GPSImgDirectionRef] == b"M": if item := is_present("GPSImgDirectionRef"):
ref = "Magnetic" if item == b"M":
elif dict[piexif.GPSIFD.GPSImgDirectionRef] == b"T": ref = "Magnetic"
ref = "True" elif item == b"T":
direction = f"Direction of view: {nsew:2} ({bearing:.0f}&deg;{ref})" ref = "True"
else:
ref =""
direction = f"Direction of view: {nsew:2} ({bearing:.0f}&deg;{ref})"
else:
direction = ""
if version := is_present("GPSVersionID"):
print(f"{dict[piexif.GPSIFD.GPSVersionID]=}") print(f"GPS exif {version=}")
if alt := extract("GPSAltitude"):
altitude = f"{alt:.0f}m above sea-level"
else:
altitude = ""
alt = extract("GPSAltitude") if ds := is_present("GPSDateStamp"):
altitude = f"{alt:.0f}m above sea-level" ds = ds.decode()
ds = dict[piexif.GPSIFD.GPSDateStamp] else:
hf, mf, sf = dict[piexif.GPSIFD.GPSTimeStamp] ds = ""
h = rational(hf)
m = rational(mf) if item := is_present("GPSTimeStamp"):
s = rational(sf) hf, mf, sf = item
h = rational(hf)
timestamp_utc = f"{ds.decode()} {h:02.0f}:{m:02.0f}:{s:02.0f} +00:00 UTC" m = rational(mf)
s = rational(sf)
timestamp_utc = f"{ds} {h:02.0f}:{m:02.0f}:{s:02.0f} +00:00 UTC"
else:
timestamp_utc = f"{ds}"
print(direction) print(direction)
print(altitude) print(altitude)
print(timestamp_utc) print(timestamp_utc)
# location = dms2dd() # location = dms2dd() # to do...
return f"{direction}<br />{altitude}</br />{timestamp_utc}<br />" return f"{direction}<br />{altitude}</br />{timestamp_utc}<br />"
@@ -176,12 +203,18 @@ def new_image_form(request, path):
directory = get_dir(path) directory = get_dir(path)
# print(f"new_image_form(): {directory=} {path=}") # print(f"new_image_form(): {directory=} {path=}")
editor = get_cookie(request) identified_login = is_identified_user(request.user)
editor = get_editor(request)
# print(f"{django_settings.FILE_UPLOAD_MAX_MEMORY_SIZE=}") # print(f"{django_settings.FILE_UPLOAD_MAX_MEMORY_SIZE=}")
# FILE_UPLOAD_MAX_MEMORY_SIZE = 0 # force uploaded files to be temporary in /tmp, not in-memory # FILE_UPLOAD_MAX_MEMORY_SIZE = 0 # force uploaded files to be temporary in /tmp, not in-memory
if request.method == "POST": if request.method == "POST":
# print(f"new_image_form(): POST ") # print(f"new_image_form(): POST ")
form = NewWebImageForm(request.POST, request.FILES, directory=directory) form = NewWebImageForm(request.POST, request.FILES, directory=directory)
if identified_login:
# disable editing the git id string as we get it from the logged-on user data
form.fields["who_are_you"].widget.attrs["readonly"]="readonly"
print(form.fields["who_are_you"].widget.attrs)
if form.is_valid(): if form.is_valid():
# print(f"new_image_form(): form is valid ") # print(f"new_image_form(): form is valid ")
year = form.cleaned_data["year"] year = form.cleaned_data["year"]
@@ -271,14 +304,15 @@ def new_image_form(request, path):
# print(full_path, full_path.parent) # print(full_path, full_path.parent)
full_path.parent.mkdir(parents=True, exist_ok=True) full_path.parent.mkdir(parents=True, exist_ok=True)
try: try:
change_message = form.cleaned_data["change_message"] # change_message = form.cleaned_data["change_message"]
write_and_commit( write_and_commit(
[ [
(desc_path, image_page, "utf-8"), (desc_path, image_page, "utf-8"),
(image_path, ib.getbuffer(), False), (image_path, ib.getbuffer(), False),
(thumb_path, tb.getbuffer(), False), (thumb_path, tb.getbuffer(), False),
], ],
f"{change_message} - online adding of an image", # f"{change_message} - online adding of an image",
f"Online adding of an image",
editor # this works, a new who_are_you typed on the Image form is used as the git comment editor # this works, a new who_are_you typed on the Image form is used as the git comment
) )
except WriteAndCommitError as e: except WriteAndCommitError as e:
@@ -302,6 +336,11 @@ def new_image_form(request, path):
"year": "", "photographer": extract_git_name(editor), "year": "", "photographer": extract_git_name(editor),
"change_message": "Uploading photo"} "change_message": "Uploading photo"}
form = NewWebImageForm(directory=directory, initial=initial ) form = NewWebImageForm(directory=directory, initial=initial )
if identified_login:
# disable editing the git id string as we get it from the logged-on user data
form.fields["who_are_you"].widget.attrs["readonly"]="readonly"
print(form.fields["who_are_you"].widget.attrs)
# print(f"new_image_form(): POST and not POST ") # print(f"new_image_form(): POST and not POST ")
template = loader.get_template("new_image_form.html") template = loader.get_template("new_image_form.html")
htmlform = template.render({"form": form, "path": path}, request) htmlform = template.render({"form": form, "path": path}, request)
@@ -322,6 +361,8 @@ def save_original_in_expofiles(f, year, photographer):
get recorded properly in original format. get recorded properly in original format.
Django does small files <2.5 MB in memory, which is a pain. Django does small files <2.5 MB in memory, which is a pain.
to do: also store a *.url file with the image file saying where it is used in the handbook.
""" """
if photographer: if photographer:
photographer = photographer.strip().replace(" ","") photographer = photographer.strip().replace(" ","")
@@ -376,15 +417,18 @@ class NewWebImageForm(forms.Form):
year = forms.CharField( year = forms.CharField(
widget=forms.TextInput(attrs={"size": "60", "placeholder": "Year photo was taken"}), required=False widget=forms.TextInput(attrs={"size": "60", "placeholder": "Year photo was taken"}), required=False
) )
change_message = forms.CharField( # change_message = forms.CharField(
widget=forms.Textarea(attrs={"cols": 80, "rows": 3, "placeholder": "Describe the change made (for git)"}), required=False # widget=forms.Textarea(attrs={"cols": 80, "rows": 3, "placeholder": "Describe the change made (for git)"}), required=False
) # )
who_are_you = forms.CharField( who_are_you = forms.CharField(
widget=forms.TextInput( widget=forms.TextInput(attrs={"style": "font-size: 90%", "size": "75",
attrs={"size": 60, "placeholder": "You are editing this page, who are you ? e.g. 'Becka' or 'Animal <mta@gasthof.expo>'", "placeholder": "Anathema Device <anathema@potatohut.expo>",
"style": "vertical-align: text-top;"} "title":"Type in your real name, and your email between angle-brackets."
}),
# label = "Editor",
) )
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.directory = Path(kwargs.pop("directory")) self.directory = Path(kwargs.pop("directory"))

View File

@@ -17,9 +17,10 @@ from troggle.core.utils import (
COOKIE_MAX_AGE, COOKIE_MAX_AGE,
WriteAndCommitError, WriteAndCommitError,
current_expo, current_expo,
get_cookie, get_editor,
git_string, # get_cookie,
get_git_string, # git_string,
# get_git_string,
write_and_commit, write_and_commit,
is_identified_user is_identified_user
) )
@@ -462,11 +463,8 @@ def editexpopage(request, path):
print("### File not found ### ", filepath) print("### File not found ### ", filepath)
filefound = False filefound = False
current_user = request.user identified_login = is_identified_user(request.user)
if identified_login := is_identified_user(current_user): editor = get_editor(request)
editor = get_git_string(current_user)
else:
editor = get_cookie(request)
if request.method == "POST": # If the form has been submitted... if request.method == "POST": # If the form has been submitted...
pageform = ExpoPageForm(request.POST) # A form bound to the POST data pageform = ExpoPageForm(request.POST) # A form bound to the POST data
@@ -503,9 +501,9 @@ def editexpopage(request, path):
print(f"Cookie set: {editor} for {COOKIE_MAX_AGE/(24*3600)} days") print(f"Cookie set: {editor} for {COOKIE_MAX_AGE/(24*3600)} days")
try: try:
change_message = pageform.cleaned_data["change_message"] change_message = pageform.cleaned_data["change_message"]
if not identified_login: # if not identified_login:
editor = pageform.cleaned_data["who_are_you"] # editor = pageform.cleaned_data["who_are_you"]
editor = git_string(editor) # editor = git_string(editor)
write_and_commit([(filepath, result, "utf-8")], f"{change_message} - online edit of {path}", editor) write_and_commit([(filepath, result, "utf-8")], f"{change_message} - online edit of {path}", editor)
except WriteAndCommitError as e: except WriteAndCommitError as e:
return render(request, "errors/generic.html", {"message": e.message}) return render(request, "errors/generic.html", {"message": e.message})
@@ -556,9 +554,10 @@ class ExpoPageForm(forms.Form):
identified_login = forms.BooleanField(required=False,widget=forms.CheckboxInput(attrs={"onclick":"return false"})) # makes it readonly identified_login = forms.BooleanField(required=False,widget=forms.CheckboxInput(attrs={"onclick":"return false"})) # makes it readonly
who_are_you = forms.CharField( who_are_you = forms.CharField(
widget=forms.Textarea( widget=forms.TextInput(attrs={"style": "font-size: 90%", "size": "75",
attrs={"cols": 90, "rows": 1, "placeholder": "You have edited this page, who are you ? e.g. 'Animal <mta@gasthof.expo>'", "placeholder": "Anathema Device <anathema@potatohut.expo>",
"style": "vertical-align: text-top;"} "title":"Type in your real name, and your email between angle-brackets."
), }),
label = "Editor" label = "Editor",
) )

View File

@@ -20,12 +20,10 @@ Passwords are only ever stored as hashes using the standard Django functions.
""" """
todo = """ todo = """
- Not fully tested, needs experience
- Need to check/register with lists.wookware.org for email - Need to check/register with lists.wookware.org for email
""" """
SUPER_USERS = ["philip-sargent"] # list of userids who get the same rights as "expoadmin" i.e. the Django control panel SUPER_USERS = ["philip-sargent", "wookey"] # list of userids who get the same rights as "expoadmin" i.e. the Django control panel
USERS_FILE = "users.json" USERS_FILE = "users.json"
ENCRYPTED_DIR = "encrypted" ENCRYPTED_DIR = "encrypted"