Compare commits
2 Commits
28d22c040f
...
ddfd29ea0e
| Author | SHA1 | Date | |
|---|---|---|---|
| ddfd29ea0e | |||
| 3f2beafd94 |
+1
-1
@@ -141,7 +141,7 @@ header {
|
|||||||
padding: var(--space-3) var(--space-4);
|
padding: var(--space-3) var(--space-4);
|
||||||
border-bottom: var(--border-dashed);
|
border-bottom: var(--border-dashed);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr minmax(0, 60rem) 1fr;
|
grid-template-columns: 1fr minmax(0, 50rem) 1fr;
|
||||||
grid-template-areas: "crumbs search actions";
|
grid-template-areas: "crumbs search actions";
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
|
|||||||
@@ -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