diff --git a/http_mutators.go b/http_mutators.go index d7569f5..31f9915 100644 --- a/http_mutators.go +++ b/http_mutators.go @@ -39,7 +39,11 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) { hyphaData, isOld = HyphaStorage[hyphaName] newName = CanonicalName(rq.PostFormValue("new-name")) _, newNameIsUsed = HyphaStorage[newName] + recursive bool ) + if rq.PostFormValue("recursive") == "true" { + recursive = true + } switch { case newNameIsUsed: HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists", @@ -54,7 +58,7 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) { 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 { + if hop := hyphaData.RenameHypha(hyphaName, newName, recursive); len(hop.Errs) == 0 { http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther) } else { HttpErr(w, http.StatusInternalServerError, hyphaName, diff --git a/hypha.go b/hypha.go index d6920a3..74a8685 100644 --- a/hypha.go +++ b/hypha.go @@ -11,6 +11,7 @@ import ( "github.com/bouncepaw/mycorrhiza/gemtext" "github.com/bouncepaw/mycorrhiza/history" + "github.com/bouncepaw/mycorrhiza/util" ) func init() { @@ -52,25 +53,61 @@ func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp { return hop } -// 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 { +func findHyphaeToRename(hyphaName string, recursive bool) []string { + hyphae := []string{hyphaName} + if recursive { + hyphae = append(hyphae, util.FindSubhyphae(hyphaName, IterateHyphaNamesWith)...) + } + return hyphae +} + +func renamingPairs(hyphaNames []string, replaceName func(string) string) map[string]string { + renameMap := make(map[string]string) + for _, hn := range hyphaNames { + if hd, ok := HyphaStorage[hn]; ok { + if hd.textPath != "" { + renameMap[hd.textPath] = replaceName(hd.textPath) + } + if hd.binaryPath != "" { + renameMap[hd.binaryPath] = replaceName(hd.binaryPath) + } + } + } + return renameMap +} + +// word Data is plural here +func relocateHyphaData(hyphaNames []string, replaceName func(string) string) { + for _, hyphaName := range hyphaNames { + if hd, ok := HyphaStorage[hyphaName]; ok { + hd.textPath = replaceName(hd.textPath) + hd.binaryPath = replaceName(hd.binaryPath) + HyphaStorage[replaceName(hyphaName)] = hd + delete(HyphaStorage, hyphaName) + } + } +} + +// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way. +func (hd *HyphaData) RenameHypha(hyphaName, newName string, recursive bool) *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() + replaceName = func(str string) string { + return strings.Replace(str, hyphaName, newName, 1) + } + hyphaNames = findHyphaeToRename(hyphaName, recursive) + renameMap = renamingPairs(hyphaNames, replaceName) + renameMsg = "Rename ‘%s’ to ‘%s’" + hop = history.Operation(history.TypeRenameHypha) ) + if recursive { + renameMsg += " recursively" + } + hop.WithFilesRenamed(renameMap). + WithMsg(fmt.Sprintf(renameMsg, hyphaName, newName)). + WithSignature("anon"). + Apply() if len(hop.Errs) == 0 { - hd.textPath = newTextPath - hd.binaryPath = newBinaryPath - HyphaStorage[newName] = hd - delete(HyphaStorage, hyphaName) + relocateHyphaData(hyphaNames, replaceName) } return hop } diff --git a/templates/rename.qtpl b/templates/rename.qtpl index 64d20e8..77e4e56 100644 --- a/templates/rename.qtpl +++ b/templates/rename.qtpl @@ -6,11 +6,20 @@ This dialog is to be shown to a user when they try to rename a hypha.

Rename {%s hyphaName %}

- - +
+ New name + +
+ +
+ Settings + + +
+ +

If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.

-

If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.

{%- else -%} {%= cannotRenameDueToNonExistence(hyphaName) %} diff --git a/templates/rename.qtpl.go b/templates/rename.qtpl.go index 29eb55e..9ae1c45 100644 --- a/templates/rename.qtpl.go +++ b/templates/rename.qtpl.go @@ -44,103 +44,112 @@ func StreamRenameAskHTML(qw422016 *qt422016.Writer, hyphaName string, isOld bool qw422016.E().S(hyphaName) //line templates/rename.qtpl:8 qw422016.N().S(`" method="post" enctype="multipart/form-data"> - - + + +
+ Settings + + +
+ +

If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.

-

If you rename this hypha, all incoming links and all relative outcoming links will break. You will also lose all history for the new name. Rename carefully.

`) -//line templates/rename.qtpl:15 +//line templates/rename.qtpl:24 } else { -//line templates/rename.qtpl:15 +//line templates/rename.qtpl:24 qw422016.N().S(` `) -//line templates/rename.qtpl:16 +//line templates/rename.qtpl:25 streamcannotRenameDueToNonExistence(qw422016, hyphaName) -//line templates/rename.qtpl:16 +//line templates/rename.qtpl:25 qw422016.N().S(` `) -//line templates/rename.qtpl:17 +//line templates/rename.qtpl:26 } -//line templates/rename.qtpl:17 +//line templates/rename.qtpl:26 qw422016.N().S(` `) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 } -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 func WriteRenameAskHTML(qq422016 qtio422016.Writer, hyphaName string, isOld bool) { -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 StreamRenameAskHTML(qw422016, hyphaName, isOld) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 qt422016.ReleaseWriter(qw422016) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 } -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 func RenameAskHTML(hyphaName string, isOld bool) string { -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 qb422016 := qt422016.AcquireByteBuffer() -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 WriteRenameAskHTML(qb422016, hyphaName, isOld) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 qs422016 := string(qb422016.B) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 qt422016.ReleaseByteBuffer(qb422016) -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 return qs422016 -//line templates/rename.qtpl:19 +//line templates/rename.qtpl:28 } -//line templates/rename.qtpl:21 +//line templates/rename.qtpl:30 func streamcannotRenameDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) { -//line templates/rename.qtpl:21 +//line templates/rename.qtpl:30 qw422016.N().S(`

Cannot rename `) -//line templates/rename.qtpl:23 +//line templates/rename.qtpl:32 qw422016.E().S(hyphaName) -//line templates/rename.qtpl:23 +//line templates/rename.qtpl:32 qw422016.N().S(`

This hypha does not exist.

Go back

`) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 } -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 func writecannotRenameDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) { -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 streamcannotRenameDueToNonExistence(qw422016, hyphaName) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 qt422016.ReleaseWriter(qw422016) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 } -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 func cannotRenameDueToNonExistence(hyphaName string) string { -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 qb422016 := qt422016.AcquireByteBuffer() -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 writecannotRenameDueToNonExistence(qb422016, hyphaName) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 qs422016 := string(qb422016.B) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 qt422016.ReleaseByteBuffer(qb422016) -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 return qs422016 -//line templates/rename.qtpl:27 +//line templates/rename.qtpl:36 } diff --git a/util/util.go b/util/util.go index ee77e07..2e556d0 100644 --- a/util/util.go +++ b/util/util.go @@ -25,3 +25,14 @@ func HTTP200Page(w http.ResponseWriter, page string) { w.WriteHeader(http.StatusOK) w.Write([]byte(page)) } + +// FindSubhyphae finds names of existing hyphae given the `hyphaIterator`. +func FindSubhyphae(hyphaName string, hyphaIterator func(func(string))) []string { + subhyphae := make([]string, 0) + hyphaIterator(func(otherHyphaName string) { + if strings.HasPrefix(otherHyphaName, hyphaName+"/") { + subhyphae = append(subhyphae, otherHyphaName) + } + }) + return subhyphae +}