diff options
-rw-r--r-- | core/models/caves.py | 12 | ||||
-rw-r--r-- | core/utils.py | 130 | ||||
-rw-r--r-- | core/views/editor_helpers.py | 23 | ||||
-rw-r--r-- | core/views/expo.py | 1 | ||||
-rw-r--r-- | core/views/uploads.py | 21 | ||||
-rw-r--r-- | templates/textfileform.html | 7 |
6 files changed, 64 insertions, 130 deletions
diff --git a/core/models/caves.py b/core/models/caves.py index 3154acb..0a910ef 100644 --- a/core/models/caves.py +++ b/core/models/caves.py @@ -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] diff --git a/core/utils.py b/core/utils.py index 26d88c6..673dcee 100644 --- a/core/utils.py +++ b/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. diff --git a/core/views/editor_helpers.py b/core/views/editor_helpers.py index 7f80a9b..ead0bc8 100644 --- a/core/views/editor_helpers.py +++ b/core/views/editor_helpers.py @@ -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) diff --git a/core/views/expo.py b/core/views/expo.py index d0b721e..d3d649b 100644 --- a/core/views/expo.py +++ b/core/views/expo.py @@ -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. diff --git a/core/views/uploads.py b/core/views/uploads.py index c7b7e74..f4e2278 100644 --- a/core/views/uploads.py +++ b/core/views/uploads.py @@ -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 diff --git a/templates/textfileform.html b/templates/textfileform.html index 3685d50..75a5131 100644 --- a/templates/textfileform.html +++ b/templates/textfileform.html @@ -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" > |