forked from expo/troggle
refactor & git author work
This commit is contained in:
@@ -11,7 +11,7 @@ import settings
|
||||
from troggle.core.models.logbooks import QM
|
||||
from troggle.core.models.survex import SurvexStation, utmToLatLng
|
||||
from troggle.core.models.troggle import DataIssue, TroggleModel
|
||||
from troggle.core.utils import TROG, parse_aliases #, writetrogglefile
|
||||
from troggle.core.utils import TROG, parse_aliases
|
||||
|
||||
# Use the TROG global object to cache the cave lookup list. No good for multi-user.., or even multi-page. Pointless in fact.
|
||||
Gcavelookup = TROG["caves"]["gcavelookup"]
|
||||
@@ -224,11 +224,6 @@ class Cave(TroggleModel):
|
||||
content = t.render(c)
|
||||
return (filepath, content, "utf8")
|
||||
|
||||
# def writeDataFile(self):
|
||||
# filepath, content, coding = self.file_output()
|
||||
# writetrogglefile(filepath, content)
|
||||
# return
|
||||
|
||||
class Entrance(TroggleModel):
|
||||
MARKING_CHOICES = (
|
||||
("P", "Paint"),
|
||||
@@ -406,11 +401,6 @@ class Entrance(TroggleModel):
|
||||
content = t.render(c)
|
||||
return (filepath, content, "utf8")
|
||||
|
||||
# def writeDataFile(self):
|
||||
# filepath, content, coding = self.file_output()
|
||||
# writetrogglefile(filepath, content)
|
||||
# return
|
||||
|
||||
def url_parent(self):
|
||||
if self.url:
|
||||
return self.url.rsplit("/", 1)[0]
|
||||
|
||||
130
core/utils.py
130
core/utils.py
@@ -127,8 +127,8 @@ def make_new_expo_dir(year):
|
||||
content = f"<html><head><title>{ff}</title></head><body><h1>{ff}</h1>{t}</body></html>"
|
||||
p = Path(year_dir, ff+".html")
|
||||
if not p.is_file():
|
||||
writetrogglefile(p, content, commit_msg="Auto new year file creation")
|
||||
|
||||
write_and_commit( [(p, content, "utf8")], f"Auto new year {ff} file creation", "Auto New Year <make_new_expo_dir@troggle.expo>")
|
||||
|
||||
|
||||
def current_expo():
|
||||
"""Returns the current expo year, but also checks if the most recent expo year is the same
|
||||
@@ -233,7 +233,7 @@ def git_add(filename, cwd, commands=[]):
|
||||
|
||||
# what is the purpose of this 'git diff' ? To prevent merge conflicts happening I guess,
|
||||
# so we do not have to reverse a 'git add'
|
||||
print(f"git diff {filename}")
|
||||
print(f"git diff {filename} in {cwd}")
|
||||
cmd_diff = [git, "diff", filename]
|
||||
commands.append(cmd_diff)
|
||||
cp_diff = subprocess.run(cmd_diff, cwd=cwd, capture_output=True, text=True)
|
||||
@@ -243,7 +243,7 @@ def git_add(filename, cwd, commands=[]):
|
||||
f"CANNOT git ADD on server for this file {filename}.\n\n" + msgdata
|
||||
)
|
||||
|
||||
print(f"git add {filename}")
|
||||
print(f"git add {filename} in {cwd}")
|
||||
cmd_add = [git, "add", filename]
|
||||
commands.append(cmd_add)
|
||||
cp_add = subprocess.run(cmd_add, cwd=cwd, capture_output=True, text=True)
|
||||
@@ -267,6 +267,11 @@ def git_commit(cwd, message, editor, commands=[]):
|
||||
cp_commit = subprocess.run(cmd_commit, cwd=cwd, capture_output=True, text=True)
|
||||
# This produces return code = 1 if it commits OK, but when the local repo still needs to be pushed to origin/repo
|
||||
# which will be the case when running a test troggle system on a development machine
|
||||
|
||||
# Several ways of testing if the commit failed
|
||||
#This produces return code = 1 if it commits OK, but when the repo still needs to be pushed to origin/expoweb
|
||||
# if (not cp_commit.stdout) or len(cp_commit.stdout) < 2 or cp_commit.stdout.split("\n")[-2] != "nothing to commit, working tree clean":
|
||||
|
||||
if cp_commit.returncode == 1 and cp_commit.stdout == DEV_OK: # only good for 1 commit ahead of origin/repo
|
||||
pass
|
||||
else:
|
||||
@@ -303,20 +308,23 @@ def add_commit(fname, message, editor=None):
|
||||
raise WriteAndCommitError(msg)
|
||||
|
||||
|
||||
def write_and_commit(files, message, editor=None):
|
||||
"""Writes the content to the filepath and adds and commits the file to git. If this fails, a WriteAndCommitError is raised.
|
||||
def write_and_commit(files, message, editor):
|
||||
"""For each file in files, it writes the content to the filepath
|
||||
and adds and commits the file to git.
|
||||
filepath, content, encoding = file
|
||||
If this fails, a WriteAndCommitError is raised.
|
||||
|
||||
This needs refactoring to just write and then call add_commit()
|
||||
This needs refactoring to just write to the filesystem and then call git_add()
|
||||
for each file, and then git_commit() at the end.
|
||||
|
||||
message - the "-m" comment field for the git commit
|
||||
editor - the "--author" field for the git commit
|
||||
"""
|
||||
# GIT see also core/views/uploads.py dwgupload()
|
||||
# GIT see also core/views/expo.py editexpopage()
|
||||
git = settings.GIT
|
||||
commands = []
|
||||
if editor:
|
||||
editor = git_string(editor)
|
||||
else:
|
||||
# cannot happen as form verification has this as an obligatory field
|
||||
editor = "Write_and_commit <automaton@potatohut.expo>"
|
||||
editor = git_string(editor)
|
||||
try:
|
||||
for filepath, content, encoding in files:
|
||||
cwd = filepath.parent
|
||||
@@ -345,56 +353,10 @@ def write_and_commit(files, message, editor=None):
|
||||
raise WriteAndCommitError(
|
||||
f"CANNOT write this file {filepath}. Ask a nerd to fix this: {e}"
|
||||
)
|
||||
|
||||
# what is the purpose of this 'git diff' ? To prevent merge conflicts happening I guess,
|
||||
# so we do not have to reverse a 'git add'
|
||||
cmd_diff = [git, "diff", filename]
|
||||
cp_diff = subprocess.run(cmd_diff, cwd=cwd, capture_output=True, text=True)
|
||||
commands.append(cmd_diff)
|
||||
if cp_diff.returncode == 0:
|
||||
cmd_add = [git, "add", filename]
|
||||
cp_add = subprocess.run(cmd_add, cwd=cwd, capture_output=True, text=True)
|
||||
commands.append(cmd_add)
|
||||
git_add_returncode = ""
|
||||
if cp_add.returncode != 0:
|
||||
git_add_returncode = cp_add.returncode
|
||||
msgdata = (
|
||||
"Ask a nerd to fix this.\n\nstderr:\n"
|
||||
+ cp_add.stderr
|
||||
+ "\n\nstdout:\n"
|
||||
+ cp_add.stdout
|
||||
+ "\n\nreturn code: "
|
||||
+ str(cp_add.returncode)
|
||||
)
|
||||
raise WriteAndCommitError(
|
||||
f"PROBLEM with git on server for {filename}. Edits saved but [possibly] not added to git.\n\n"
|
||||
+ msgdata
|
||||
)
|
||||
else:
|
||||
print(f"No change {filepath}")
|
||||
filepaths = [filepath for filepath, content, encoding in files]
|
||||
# message = message + " " + str(filepaths)
|
||||
|
||||
commands = git_add(filename, cwd, commands)
|
||||
|
||||
commands = git_commit(cwd, message, editor, commands)
|
||||
|
||||
if False:
|
||||
cmd_status = [git, "status"] # + filepaths
|
||||
cp_status = subprocess.run(cmd_status, cwd=cwd, capture_output=True, text=True)
|
||||
commands.append(cp_status)
|
||||
#This produces return code = 1 if it commits OK, but when the repo still needs to be pushed to origin/expoweb
|
||||
if (not cp_status.stdout) or len(cp_status.stdout) < 2 or cp_status.stdout.split("\n")[-2] != "nothing to commit, working tree clean":
|
||||
msgdata = (
|
||||
str(commands) +
|
||||
"Ask a nerd to fix this.\n\n"
|
||||
+ "Stderr: " + cp_status.stderr
|
||||
+ "\n\n"
|
||||
+ "Stdout: " + cp_status.stdout
|
||||
+ "\n\nreturn code: " + str(cp_status.returncode)
|
||||
)
|
||||
raise WriteAndCommitError(
|
||||
f"Error code with git on server for this file {filename}. Edits saved, added to git, but NOT committed. Git status not clean.\n\n"
|
||||
+ msgdata
|
||||
)
|
||||
except subprocess.SubprocessError:
|
||||
raise WriteAndCommitError(
|
||||
f"CANNOT git on server for this file {filename}. Subprocess error. Edits not saved.\nAsk a nerd to fix this."
|
||||
@@ -412,54 +374,6 @@ class WriteAndCommitError(Exception):
|
||||
return f"WriteAndCommitError: {self.message}"
|
||||
|
||||
|
||||
def writetrogglefile(filepath, filecontent, commit_msg=None):
|
||||
"""
|
||||
REPLACE with call to write_and_commit + any necessary setup
|
||||
used only by cave editor in
|
||||
core/models/caves.py
|
||||
and by
|
||||
make_new_expo_dir(year)
|
||||
but NOT by core/views/caves.py
|
||||
|
||||
Commit the new saved file to git
|
||||
|
||||
"""
|
||||
# GIT see also core/views/expo.py editexpopage()
|
||||
# GIT see also core/views/uploads.py dwgupload()
|
||||
filepath = Path(filepath)
|
||||
cwd = filepath.parent
|
||||
filename = filepath.name
|
||||
git = settings.GIT
|
||||
|
||||
# do not trap exceptions, pass them up to the view that called this function
|
||||
print(f"WRITING{cwd}---{filename} ")
|
||||
with open(filepath, "w") as f:
|
||||
f.write(filecontent)
|
||||
# os.chmod(filepath, 0o664) # set file permissions to rw-rw-r--
|
||||
sp = subprocess.run([git, "add", filename], cwd=cwd, capture_output=True, check=True, text=True)
|
||||
if sp.returncode != 0:
|
||||
out = sp.stdout
|
||||
if len(out) > 160:
|
||||
out = out[:75] + "\n <Long output curtailed>\n" + out[-75:]
|
||||
print(f"git ADD {cwd}:\n\n" + str(sp.stderr) + "\n\n" + out + "\n\nreturn code: " + str(sp.returncode))
|
||||
|
||||
if not commit_msg:
|
||||
commit_msg = f"Troggle online: cave or entrance edit -{filename}"
|
||||
sp = subprocess.run(
|
||||
[git, "commit", "-m", commit_msg],
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
text=True,
|
||||
)
|
||||
if sp.returncode != 0:
|
||||
out = sp.stdout
|
||||
if len(out) > 160:
|
||||
out = out[:75] + "\n <Long output curtailed>\n" + out[-75:]
|
||||
print(f"git COMMIT {cwd}:\n\n" + str(sp.stderr) + "\n\n" + out + "\n\nreturn code: " + str(sp.returncode))
|
||||
# not catching and re-raising any exceptions yet, inc. the stderr etc.,. We should do that.
|
||||
|
||||
|
||||
"""The following is a Bard converted version of Radosts's MIT copyrighted Javascript on 2023-10-27
|
||||
with hand-editing.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from PIL import Image
|
||||
|
||||
import troggle.settings as settings
|
||||
from troggle.core.utils import WriteAndCommitError, write_and_commit
|
||||
from troggle.core.utils import COOKIE_MAX_AGE, WriteAndCommitError, get_cookie, git_string, write_and_commit
|
||||
|
||||
from .auth import login_required_if_public
|
||||
|
||||
@@ -97,12 +97,16 @@ def new_image_form(request, path):
|
||||
"""Manages a form to upload new images"""
|
||||
directory = get_dir(path)
|
||||
print(f"new_image_form(): {directory=} {path=}")
|
||||
|
||||
editor = get_cookie(request)
|
||||
if request.method == "POST":
|
||||
print(f"new_image_form(): POST ")
|
||||
form = NewWebImageForm(request.POST, request.FILES, directory=directory)
|
||||
if form.is_valid():
|
||||
print(f"new_image_form(): form is valid ")
|
||||
print(f"new_image_form(): files: {request.FILES['file_']}")
|
||||
editor = form.cleaned_data["who_are_you"]
|
||||
editor = git_string(editor)
|
||||
f = request.FILES["file_"]
|
||||
binary_data = io.BytesIO()
|
||||
for chunk in f.chunks():
|
||||
@@ -171,6 +175,7 @@ def new_image_form(request, path):
|
||||
(thumb_path, tb.getbuffer(), False),
|
||||
],
|
||||
f"{change_message} - online adding of an image",
|
||||
editor # this works, a new who_are_you typed on the Image form is used as the git comment
|
||||
)
|
||||
except WriteAndCommitError as e:
|
||||
print(f"new_image_form(): WriteAndCommitError: {e.message}")
|
||||
@@ -183,10 +188,12 @@ def new_image_form(request, path):
|
||||
html_snippet = linked_image_template.render(
|
||||
{"thumbnail_url": f"/{thumb_rel_path}", "page_url": f"/{desc_rel_path}"}, request
|
||||
)
|
||||
return JsonResponse({"html": html_snippet})
|
||||
j_response = JsonResponse({"html": html_snippet})
|
||||
j_response.set_cookie('editor_id', editor, max_age=COOKIE_MAX_AGE) # does not seem to work updating who_are_you cookie
|
||||
return j_response
|
||||
else:
|
||||
print(f"new_image_form(): not POST ")
|
||||
form = NewWebImageForm(directory=directory)
|
||||
form = NewWebImageForm(directory=directory, initial={"who_are_you":editor})
|
||||
print(f"new_image_form(): POST and not POST ")
|
||||
template = loader.get_template("new_image_form.html")
|
||||
htmlform = template.render({"form": form, "path": path}, request)
|
||||
@@ -212,9 +219,15 @@ class NewWebImageForm(forms.Form):
|
||||
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)"})
|
||||
widget=forms.Textarea(attrs={"cols": 80, "rows": 3, "placeholder": "Describe the change made (for git)"})
|
||||
)
|
||||
|
||||
who_are_you = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={"size": 60, "placeholder": "You are editing this page, who are you ? e.g. 'Becka' or 'Animal <mta@gasthof.expo>'",
|
||||
"style": "vertical-align: text-top;"}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.directory = Path(kwargs.pop("directory"))
|
||||
super(forms.Form, self).__init__(*args, **kwargs)
|
||||
|
||||
@@ -80,7 +80,6 @@ def mapfile(request, path):
|
||||
return render(request, "errors/generic.html", {"message": message})
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
@@ -12,7 +12,7 @@ from troggle.core.models.caves import GetCaveLookup
|
||||
from troggle.core.models.logbooks import LogbookEntry, PersonLogEntry, writelogbook
|
||||
from troggle.core.models.survex import DrawingFile
|
||||
from troggle.core.models.troggle import DataIssue, Expedition, PersonExpedition
|
||||
from troggle.core.utils import alphabet_suffix, current_expo, sanitize_name, unique_slug, write_and_commit
|
||||
from troggle.core.utils import COOKIE_MAX_AGE, alphabet_suffix, current_expo, get_cookie, git_string, sanitize_name, unique_slug, write_and_commit
|
||||
from troggle.parsers.people import GetPersonExpeditionNameLookup, known_foreigner
|
||||
|
||||
# from databaseReset import reinit_db # don't do this. databaseRest runs code *at import time*
|
||||
@@ -150,6 +150,12 @@ class ExpofileRenameForm(forms.Form): # not a model-form, just a form-form
|
||||
|
||||
class ExpotextfileForm(forms.Form): # not a model-form, just a form-form
|
||||
text = forms.CharField(strip=True, required=False)
|
||||
who_are_you = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={"size": 100, "placeholder": "You are editing this page, who are you ? e.g. 'Wookey' or 'Animal <mta@gasthof.expo>'",
|
||||
"style": "vertical-align: text-top;"}
|
||||
)
|
||||
)
|
||||
|
||||
class LogbookEditForm(forms.Form): # not a model-form, just a form-form
|
||||
author = forms.CharField(strip=True, required=False)
|
||||
@@ -160,7 +166,8 @@ def edittxtpage(request, path, filepath):
|
||||
Yes this is a security hazard as arbitrary text can be uploaded and it is not enclosed in any HTML furniture.
|
||||
"""
|
||||
def simple_get(viewtext):
|
||||
form = ExpotextfileForm()
|
||||
print(f"simple_get {editor=}")
|
||||
form = ExpotextfileForm(initial={"who_are_you":editor})
|
||||
return render(
|
||||
request,
|
||||
"textfileform.html",
|
||||
@@ -188,6 +195,7 @@ def edittxtpage(request, path, filepath):
|
||||
print(message)
|
||||
return render(request, "errors/generic.html", {"message": message})
|
||||
|
||||
editor = get_cookie(request)
|
||||
if request.method == "GET":
|
||||
return simple_get(originaltext)
|
||||
|
||||
@@ -198,6 +206,9 @@ def edittxtpage(request, path, filepath):
|
||||
print(message)
|
||||
return render(request, "errors/generic.html", {"message": message})
|
||||
else:
|
||||
editor = form.cleaned_data["who_are_you"]
|
||||
editor = git_string(editor)
|
||||
|
||||
# for i in request.POST:
|
||||
# print(":: ",i, " => ", request.POST[i])
|
||||
newtext = request.POST["text"]
|
||||
@@ -215,7 +226,7 @@ def edittxtpage(request, path, filepath):
|
||||
if newtext != originaltext: # Check if content has changed at all
|
||||
print("text changed.. saving and committing")
|
||||
try:
|
||||
write_and_commit([(filepath, newtext, "utf-8")], f"Online edit of {path}")
|
||||
write_and_commit([(filepath, newtext, "utf-8")], f"Online edit of {path}", editor)
|
||||
except WriteAndCommitError as e:
|
||||
return render(request, "errors/generic.html", {"message": e.message})
|
||||
|
||||
@@ -228,7 +239,9 @@ def edittxtpage(request, path, filepath):
|
||||
return render(request, "errors/generic.html", {"message": e.message})
|
||||
savepath = "/" + path
|
||||
print(f"redirect {savepath}")
|
||||
return redirect(savepath) # Redirect after POST
|
||||
response = redirect(savepath) # Redirect after POST
|
||||
response.set_cookie('editor_id', editor, max_age=COOKIE_MAX_AGE) # cookie expires after COOKIE_MAX_AGE seconds
|
||||
return response
|
||||
|
||||
else:
|
||||
# no changes
|
||||
|
||||
@@ -26,8 +26,13 @@ Full path on server: {{filepath}}
|
||||
required />{% if text %}{{text}}{% else %}Placeholder text{% endif %}
|
||||
</textarea>
|
||||
<br>
|
||||
[Edit the text by just typing in the box.]
|
||||
[Edit the text by just typing in the box.]
|
||||
|
||||
<br /><br />
|
||||
<div>
|
||||
<label for="id_who_are_you">Who are you:</label><br />
|
||||
{{form.who_are_you}}
|
||||
</div><br />
|
||||
|
||||
<button class="fancybutton2" style="padding: 0.5em 25px; margin-left: 110px" name="Save" type = "submit"
|
||||
title="Saves the file in UTF-8 characterset" value = "Save" >
|
||||
|
||||
Reference in New Issue
Block a user