1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +00:00

Support recursive renaming

This commit is contained in:
bouncepaw 2020-10-03 21:56:56 +05:00
parent 662a52296c
commit 62fcd6d9bc
5 changed files with 133 additions and 63 deletions

View File

@ -39,7 +39,11 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
hyphaData, isOld = HyphaStorage[hyphaName] hyphaData, isOld = HyphaStorage[hyphaName]
newName = CanonicalName(rq.PostFormValue("new-name")) newName = CanonicalName(rq.PostFormValue("new-name"))
_, newNameIsUsed = HyphaStorage[newName] _, newNameIsUsed = HyphaStorage[newName]
recursive bool
) )
if rq.PostFormValue("recursive") == "true" {
recursive = true
}
switch { switch {
case newNameIsUsed: case newNameIsUsed:
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists", 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", HttpErr(w, http.StatusBadRequest, hyphaName, "Error: invalid name",
"Invalid new name. Names cannot contain characters <code>^?!:#@&gt;&lt;*|\"\\'&amp;%</code>") "Invalid new name. Names cannot contain characters <code>^?!:#@&gt;&lt;*|\"\\'&amp;%</code>")
default: 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) http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
} else { } else {
HttpErr(w, http.StatusInternalServerError, hyphaName, HttpErr(w, http.StatusInternalServerError, hyphaName,

View File

@ -11,6 +11,7 @@ import (
"github.com/bouncepaw/mycorrhiza/gemtext" "github.com/bouncepaw/mycorrhiza/gemtext"
"github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/util"
) )
func init() { func init() {
@ -52,25 +53,61 @@ func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp {
return hop return hop
} }
// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. func findHyphaeToRename(hyphaName string, recursive bool) []string {
func (hd *HyphaData) RenameHypha(hyphaName, newName string) *history.HistoryOp { 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 ( var (
newTextPath = strings.Replace(hd.textPath, hyphaName, newName, 1) replaceName = func(str string) string {
newBinaryPath = strings.Replace(hd.binaryPath, hyphaName, newName, 1) return strings.Replace(str, hyphaName, newName, 1)
hop = history.Operation(history.TypeRenameHypha). }
WithFilesRenamed(map[string]string{ hyphaNames = findHyphaeToRename(hyphaName, recursive)
hd.textPath: newTextPath, renameMap = renamingPairs(hyphaNames, replaceName)
hd.binaryPath: newBinaryPath, renameMsg = "Rename %s to %s"
}). hop = history.Operation(history.TypeRenameHypha)
WithMsg(fmt.Sprintf("Rename %s to %s", hyphaName, newName)). )
if recursive {
renameMsg += " recursively"
}
hop.WithFilesRenamed(renameMap).
WithMsg(fmt.Sprintf(renameMsg, hyphaName, newName)).
WithSignature("anon"). WithSignature("anon").
Apply() Apply()
)
if len(hop.Errs) == 0 { if len(hop.Errs) == 0 {
hd.textPath = newTextPath relocateHyphaData(hyphaNames, replaceName)
hd.binaryPath = newBinaryPath
HyphaStorage[newName] = hd
delete(HyphaStorage, hyphaName)
} }
return hop return hop
} }

View File

@ -6,11 +6,20 @@ This dialog is to be shown to a user when they try to rename a hypha.
<section> <section>
<h1>Rename {%s hyphaName %}</h1> <h1>Rename {%s hyphaName %}</h1>
<form action="/rename-confirm/{%s hyphaName %}" method="post" enctype="multipart/form-data"> <form action="/rename-confirm/{%s hyphaName %}" method="post" enctype="multipart/form-data">
<label for="new-name">New name:</label> <fieldset>
<legend>New name</legend>
<input type="text" value="{%s hyphaName %}" required autofocus id="new-name" name="new-name"/> <input type="text" value="{%s hyphaName %}" required autofocus id="new-name" name="new-name"/>
</fieldset>
<fieldset>
<legend>Settings</legend>
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
<label for="recursive">Keep subhyphae</label>
</fieldset>
<p>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.</p>
<input type="submit"/> <input type="submit"/>
</form> </form>
<p>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.</p>
</section> </section>
{%- else -%} {%- else -%}
{%= cannotRenameDueToNonExistence(hyphaName) %} {%= cannotRenameDueToNonExistence(hyphaName) %}

View File

@ -44,103 +44,112 @@ func StreamRenameAskHTML(qw422016 *qt422016.Writer, hyphaName string, isOld bool
qw422016.E().S(hyphaName) qw422016.E().S(hyphaName)
//line templates/rename.qtpl:8 //line templates/rename.qtpl:8
qw422016.N().S(`" method="post" enctype="multipart/form-data"> qw422016.N().S(`" method="post" enctype="multipart/form-data">
<label for="new-name">New name:</label> <fieldset>
<legend>New name</legend>
<input type="text" value="`) <input type="text" value="`)
//line templates/rename.qtpl:10 //line templates/rename.qtpl:11
qw422016.E().S(hyphaName) qw422016.E().S(hyphaName)
//line templates/rename.qtpl:10 //line templates/rename.qtpl:11
qw422016.N().S(`" required autofocus id="new-name" name="new-name"/> qw422016.N().S(`" required autofocus id="new-name" name="new-name"/>
</fieldset>
<fieldset>
<legend>Settings</legend>
<input type="checkbox" id="recursive" name="recursive" value="true" checked/>
<label for="recursive">Keep subhyphae</label>
</fieldset>
<p>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.</p>
<input type="submit"/> <input type="submit"/>
</form> </form>
<p>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.</p>
</section> </section>
`) `)
//line templates/rename.qtpl:15 //line templates/rename.qtpl:24
} else { } else {
//line templates/rename.qtpl:15 //line templates/rename.qtpl:24
qw422016.N().S(` `) qw422016.N().S(` `)
//line templates/rename.qtpl:16 //line templates/rename.qtpl:25
streamcannotRenameDueToNonExistence(qw422016, hyphaName) streamcannotRenameDueToNonExistence(qw422016, hyphaName)
//line templates/rename.qtpl:16 //line templates/rename.qtpl:25
qw422016.N().S(` 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(`</main> qw422016.N().S(`</main>
`) `)
//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) { func WriteRenameAskHTML(qq422016 qtio422016.Writer, hyphaName string, isOld bool) {
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
StreamRenameAskHTML(qw422016, hyphaName, isOld) StreamRenameAskHTML(qw422016, hyphaName, isOld)
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
qt422016.ReleaseWriter(qw422016) 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 { func RenameAskHTML(hyphaName string, isOld bool) string {
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
WriteRenameAskHTML(qb422016, hyphaName, isOld) WriteRenameAskHTML(qb422016, hyphaName, isOld)
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line templates/rename.qtpl:19 //line templates/rename.qtpl:28
return qs422016 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) { func streamcannotRenameDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) {
//line templates/rename.qtpl:21 //line templates/rename.qtpl:30
qw422016.N().S(` qw422016.N().S(`
<section> <section>
<h1>Cannot rename `) <h1>Cannot rename `)
//line templates/rename.qtpl:23 //line templates/rename.qtpl:32
qw422016.E().S(hyphaName) qw422016.E().S(hyphaName)
//line templates/rename.qtpl:23 //line templates/rename.qtpl:32
qw422016.N().S(`</h1> qw422016.N().S(`</h1>
<p>This hypha does not exist.</p> <p>This hypha does not exist.</p>
<p><a href="/page/`) <p><a href="/page/`)
//line templates/rename.qtpl:25 //line templates/rename.qtpl:34
qw422016.E().S(hyphaName) qw422016.E().S(hyphaName)
//line templates/rename.qtpl:25 //line templates/rename.qtpl:34
qw422016.N().S(`">Go back</a></p> qw422016.N().S(`">Go back</a></p>
</section> </section>
`) `)
//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) { func writecannotRenameDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) {
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
streamcannotRenameDueToNonExistence(qw422016, hyphaName) streamcannotRenameDueToNonExistence(qw422016, hyphaName)
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
qt422016.ReleaseWriter(qw422016) 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 { func cannotRenameDueToNonExistence(hyphaName string) string {
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
writecannotRenameDueToNonExistence(qb422016, hyphaName) writecannotRenameDueToNonExistence(qb422016, hyphaName)
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
return qs422016 return qs422016
//line templates/rename.qtpl:27 //line templates/rename.qtpl:36
} }

View File

@ -25,3 +25,14 @@ func HTTP200Page(w http.ResponseWriter, page string) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(page)) 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
}