diff --git a/README.md b/README.md index b1ea98a..c7b62f0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A wiki engine. This is a development branch for 0.9 version. Features I want to implement in this release: * [x] Recent changes page. * [x] Hypha deletion. -* [ ] Hypha renaming. +* [x] Hypha renaming. * [x] Support async git ops. ## Installation diff --git a/history/operations.go b/history/operations.go index 149e795..7b94436 100644 --- a/history/operations.go +++ b/history/operations.go @@ -64,6 +64,16 @@ func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp { return hop.gitop(args...) } +// WithFilesRenamed git-mv-s all passed keys of `pairs` to values of `pairs`. Paths can be rooted ot not. Empty keys are ignored. +func (hop *HistoryOp) WithFilesRenamed(pairs map[string]string) *HistoryOp { + for from, to := range pairs { + if from != "" { + hop.gitop(append([]string{"mv"}, from, to)...) + } + } + return hop +} + // WithFiles stages all passed `paths`. Paths can be rooted or not. func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp { for i, path := range paths { diff --git a/http_mutators.go b/http_mutators.go index 3557a87..d7569f5 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -19,6 +19,49 @@ func init() { http.HandleFunc("/edit/", handlerEdit) http.HandleFunc("/delete-ask/", handlerDeleteAsk) http.HandleFunc("/delete-confirm/", handlerDeleteConfirm) + http.HandleFunc("/rename-ask/", handlerRenameAsk) + http.HandleFunc("/rename-confirm/", handlerRenameConfirm) +} + +func handlerRenameAsk(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + var ( + hyphaName = HyphaNameFromRq(rq, "rename-ask") + _, isOld = HyphaStorage[hyphaName] + ) + util.HTTP200Page(w, base("Rename "+hyphaName+"?", templates.RenameAskHTML(hyphaName, isOld))) +} + +func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + var ( + hyphaName = HyphaNameFromRq(rq, "rename-confirm") + hyphaData, isOld = HyphaStorage[hyphaName] + newName = CanonicalName(rq.PostFormValue("new-name")) + _, newNameIsUsed = HyphaStorage[newName] + ) + switch { + case newNameIsUsed: + HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists", + fmt.Sprintf("Hypha named %s already exists.", hyphaName, hyphaName)) + case newName == "": + HttpErr(w, http.StatusBadRequest, hyphaName, "Error: no name", + "No new name is given.") + case !isOld: + HttpErr(w, http.StatusBadRequest, hyphaName, "Error: no such hypha", + "Cannot rename a hypha that does not exist yet.") + case !HyphaPattern.MatchString(newName): + HttpErr(w, http.StatusBadRequest, hyphaName, "Error: invalid name", + "Invalid new name. Names cannot contain characters ^?!:#@><*|\"\\'&%") + default: + if hop := hyphaData.RenameHypha(hyphaName, newName); len(hop.Errs) == 0 { + http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther) + } else { + HttpErr(w, http.StatusInternalServerError, hyphaName, + "Error: could not rename hypha", + fmt.Sprintf("Could not rename this hypha due to an internal error. Server errors: %v", hop.Errs)) + } + } } // handlerDeleteAsk shows a delete dialog. diff --git a/hypha.go b/hypha.go index 7a37cb9..5c9bb9e 100644 --- a/hypha.go +++ b/hypha.go @@ -7,6 +7,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/bouncepaw/mycorrhiza/gemtext" "github.com/bouncepaw/mycorrhiza/history" @@ -47,6 +48,29 @@ func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp { Apply() } +// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. +func (hd *HyphaData) RenameHypha(hyphaName, newName string) *history.HistoryOp { + var ( + newTextPath = strings.Replace(hd.textPath, hyphaName, newName, 1) + newBinaryPath = strings.Replace(hd.binaryPath, hyphaName, newName, 1) + hop = history.Operation(history.TypeRenameHypha). + WithFilesRenamed(map[string]string{ + hd.textPath: newTextPath, + hd.binaryPath: newBinaryPath, + }). + WithMsg(fmt.Sprintf("Rename ā€˜%sā€™ to ā€˜%sā€™", hyphaName, newName)). + WithSignature("anon"). + Apply() + ) + if len(hop.Errs) == 0 { + hd.textPath = newTextPath + hd.binaryPath = newBinaryPath + HyphaStorage[newName] = hd + delete(HyphaStorage, hyphaName) + } + return hop +} + // binaryHtmlBlock creates an html block for binary part of the hypha. func binaryHtmlBlock(hyphaName string, d *HyphaData) string { switch d.binaryType { diff --git a/main.go b/main.go index 981ab4b..7956270 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,7 @@ import ( // WikiDir is a rooted path to the wiki storage directory. var WikiDir string -// HyphaPattern is a pattern which all hyphae must match. Not used currently. +// HyphaPattern is a pattern which all hyphae must match. var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%]+`) // HyphaStorage is a mapping between canonical hypha names and their meta information. @@ -120,7 +120,7 @@ func main() { http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static")))) // See http_readers.go for /page/, /text/, /binary/, /history/. - // See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/. + // See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/, /rename-ask/, /rename-confirm/. http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) http.HandleFunc("/random", handlerRandom) diff --git a/metarrhiza b/metarrhiza index 2c0e431..c21b477 160000 --- a/metarrhiza +++ b/metarrhiza @@ -1 +1 @@ -Subproject commit 2c0e43199ed28f7022a38463a0eec3af3ecb03c9 +Subproject commit c21b47739bc149456acc205e2c5acfa4b9eeb9d7 diff --git a/templates/common.qtpl b/templates/common.qtpl new file mode 100644 index 0000000..c199fa2 --- /dev/null +++ b/templates/common.qtpl @@ -0,0 +1,32 @@ +This is the