Prevent page deletion on save
This commit is contained in:
@@ -426,9 +426,14 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
|
|||||||
}
|
}
|
||||||
rawMD, _ := os.ReadFile(indexPath)
|
rawMD, _ := os.ReadFile(indexPath)
|
||||||
sections := splitSections(rawMD)
|
sections := splitSections(rawMD)
|
||||||
if sectionIndex < len(sections) {
|
// Out of range means the file changed under the editor (or the index
|
||||||
sections[sectionIndex] = []byte(content)
|
// never matched it). Writing back the untouched file would swallow the
|
||||||
|
// edit silently, so refuse and keep the editor's content in the browser.
|
||||||
|
if sectionIndex >= len(sections) {
|
||||||
|
http.Error(w, "section no longer exists — the page changed since you opened the editor", http.StatusConflict)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
sections[sectionIndex] = []byte(content)
|
||||||
content = string(joinSections(sections))
|
content = string(joinSections(sections))
|
||||||
// Section index ≥ 1 is a heading-anchored section. Redirect to its
|
// Section index ≥ 1 is a heading-anchored section. Redirect to its
|
||||||
// anchor so the user lands on the section they just saved, even if
|
// anchor so the user lands on the section they just saved, even if
|
||||||
@@ -441,12 +446,15 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A save must never remove a page. An empty POST — a truncated mobile
|
||||||
|
// request, a lost `section` field, an editor that came up blank — is
|
||||||
|
// indistinguishable from "clear this page", and deleting index.md on that
|
||||||
|
// signal loses the whole file even though the user only edited one section.
|
||||||
|
// Removing a page is the explicit ?delete action's job (moves.go).
|
||||||
if strings.TrimSpace(content) == "" {
|
if strings.TrimSpace(content) == "" {
|
||||||
if err := os.Remove(indexPath); err != nil && !os.IsNotExist(err) {
|
http.Error(w, "refusing to save empty content — use DELETE to remove this page", http.StatusBadRequest)
|
||||||
http.Error(w, "delete failed: "+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Stat first so we know whether MkdirAll actually created the folder
|
// Stat first so we know whether MkdirAll actually created the folder
|
||||||
// — if it did, the search index needs a new entry.
|
// — if it did, the search index needs a new entry.
|
||||||
_, statErr := os.Stat(fsPath)
|
_, statErr := os.Stat(fsPath)
|
||||||
@@ -464,7 +472,6 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
|
|||||||
folderIndexAdd(filepath.ToSlash(rel))
|
folderIndexAdd(filepath.ToSlash(rel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// The editor saves via fetch so the save and its result share one history
|
// The editor saves via fetch so the save and its result share one history
|
||||||
// entry (see assets/history-nav.js). Hand it the target instead of a 303:
|
// entry (see assets/history-nav.js). Hand it the target instead of a 303:
|
||||||
// the browser would follow the redirect into a second entry, and fetch
|
// the browser would follow the redirect into a second entry, and fetch
|
||||||
|
|||||||
Reference in New Issue
Block a user