diff --git a/history/information.go b/history/information.go
index bb20e6a..03d6aae 100644
--- a/history/information.go
+++ b/history/information.go
@@ -43,7 +43,9 @@ func Revisions(hyphaName string) ([]Revision, error) {
)
if err == nil {
for _, line := range strings.Split(out.String(), "\n") {
- revs = append(revs, parseRevisionLine(line))
+ if line != "" {
+ revs = append(revs, parseRevisionLine(line))
+ }
}
}
return revs, err
diff --git a/history/operations.go b/history/operations.go
index 3567d78..fecee81 100644
--- a/history/operations.go
+++ b/history/operations.go
@@ -15,6 +15,8 @@ const (
TypeNone OpType = iota
TypeEditText
TypeEditBinary
+ TypeDeleteHypha
+ TypeRenameHypha
)
// HistoryOp is an object representing a history operation.
@@ -46,6 +48,17 @@ func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
return hop
}
+// WithFilesRemoved git-rm-s all passed `paths`. Paths can be rooted or not. Paths that are empty strings are ignored.
+func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
+ args := []string{"rm", "--quiet", "--"}
+ for _, path := range paths {
+ if path != "" {
+ args = append(args, path)
+ }
+ }
+ return hop.gitop(args...)
+}
+
// 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 e3cd397..3557a87 100644
--- a/http_mutators.go
+++ b/http_mutators.go
@@ -17,6 +17,42 @@ func init() {
http.HandleFunc("/upload-binary/", handlerUploadBinary)
http.HandleFunc("/upload-text/", handlerUploadText)
http.HandleFunc("/edit/", handlerEdit)
+ http.HandleFunc("/delete-ask/", handlerDeleteAsk)
+ http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
+}
+
+// handlerDeleteAsk shows a delete dialog.
+func handlerDeleteAsk(w http.ResponseWriter, rq *http.Request) {
+ log.Println(rq.URL)
+ var (
+ hyphaName = HyphaNameFromRq(rq, "delete-ask")
+ _, isOld = HyphaStorage[hyphaName]
+ )
+ util.HTTP200Page(w, base("Delete "+hyphaName+"?", templates.DeleteAskHTML(hyphaName, isOld)))
+}
+
+// handlerDeleteConfirm deletes a hypha for sure
+func handlerDeleteConfirm(w http.ResponseWriter, rq *http.Request) {
+ log.Println(rq.URL)
+ var (
+ hyphaName = HyphaNameFromRq(rq, "delete-confirm")
+ hyphaData, isOld = HyphaStorage[hyphaName]
+ )
+ if isOld {
+ // If deleted successfully
+ if hop := hyphaData.DeleteHypha(hyphaName); len(hop.Errs) == 0 {
+ http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
+ } else {
+ HttpErr(w, http.StatusInternalServerError, hyphaName,
+ "Error: could not delete hypha",
+ fmt.Sprintf("Could not delete this hypha due to an internal error. Server errors: %v
", hop.Errs))
+ }
+ } else {
+ // The precondition is to have the hypha in the first place.
+ HttpErr(w, http.StatusPreconditionFailed, hyphaName,
+ "Error: no such hypha",
+ "Could not delete this hypha because it does not exist.")
+ }
}
// handlerEdit shows the edit form. It doesn't edit anything actually.
diff --git a/http_readers.go b/http_readers.go
index f288210..5843e4d 100644
--- a/http_readers.go
+++ b/http_readers.go
@@ -6,7 +6,6 @@ import (
"log"
"net/http"
"os"
- "path"
"strings"
"github.com/bouncepaw/mycorrhiza/gemtext"
@@ -29,8 +28,9 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
var (
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
- revHash = path.Dir(shorterUrl)
- hyphaName = CanonicalName(strings.TrimPrefix(shorterUrl, revHash+"/"))
+ firstSlashIndex = strings.IndexRune(shorterUrl, '/')
+ revHash = shorterUrl[:firstSlashIndex]
+ hyphaName = CanonicalName(shorterUrl[firstSlashIndex+1:])
contents = fmt.Sprintf(`
This hypha had no text at this revision.
`)
textPath = hyphaName + "&.gmi"
textContents, err = history.FileAtRevision(textPath, revHash)
@@ -55,15 +55,15 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
hyphaName := HyphaNameFromRq(rq, "history")
var tbody string
- if _, ok := HyphaStorage[hyphaName]; ok {
- revs, err := history.Revisions(hyphaName)
- if err == nil {
- for _, rev := range revs {
- tbody += rev.AsHtmlTableRow(hyphaName)
- }
+
+ // History can be found for files that do not exist anymore.
+ revs, err := history.Revisions(hyphaName)
+ if err == nil {
+ for _, rev := range revs {
+ tbody += rev.AsHtmlTableRow(hyphaName)
}
- log.Println(revs)
}
+ log.Println("Found", len(revs), "revisions for", hyphaName)
util.HTTP200Page(w,
base(hyphaName, templates.HistoryHTML(hyphaName, tbody)))
diff --git a/hypha.go b/hypha.go
index d3be116..7a37cb9 100644
--- a/hypha.go
+++ b/hypha.go
@@ -9,6 +9,7 @@ import (
"path/filepath"
"github.com/bouncepaw/mycorrhiza/gemtext"
+ "github.com/bouncepaw/mycorrhiza/history"
)
func init() {
@@ -37,6 +38,15 @@ type HyphaData struct {
binaryType BinaryType
}
+// DeleteHypha deletes hypha and makes a history record about that.
+func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp {
+ return history.Operation(history.TypeDeleteHypha).
+ WithFilesRemoved(hd.textPath, hd.binaryPath).
+ WithMsg(fmt.Sprintf("Delete ā%sā", hyphaName)).
+ WithSignature("anon").
+ Apply()
+}
+
// 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 5ef555d..981ab4b 100644
--- a/main.go
+++ b/main.go
@@ -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/.
+ // See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/.
http.HandleFunc("/list", handlerList)
http.HandleFunc("/reindex", handlerReindex)
http.HandleFunc("/random", handlerRandom)
diff --git a/metarrhiza b/metarrhiza
index 2c0e431..ecaa76f 160000
--- a/metarrhiza
+++ b/metarrhiza
@@ -1 +1 @@
-Subproject commit 2c0e43199ed28f7022a38463a0eec3af3ecb03c9
+Subproject commit ecaa76f841afcb3514b7061eb6708092bc17ee08
diff --git a/templates/http_delete.qtpl b/templates/http_delete.qtpl
new file mode 100644
index 0000000..4044c09
--- /dev/null
+++ b/templates/http_delete.qtpl
@@ -0,0 +1,33 @@
+This dialog is to be shown to a user when they try to delete a hypha.
+{% func DeleteAskHTML(hyphaName string, isOld bool) %}
+
+
+{% if isOld %}
+
+ Delete {%s hyphaName %}?
+ Do you really want to delete hypha {%s hyphaName %}?
+ In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.
+ Confirm
+ Cancel
+
+{% else %}
+ {%= cannotDeleteDueToNonExistence(hyphaName) %}
+{% endif %}
+
+{% endfunc %}
+
+{% func cannotDeleteDueToNonExistence(hyphaName string) %}
+
+ Cannot delete {%s hyphaName %}
+ This hypha does not exist.
+ Go back
+
+{% endfunc %}
diff --git a/templates/http_delete.qtpl.go b/templates/http_delete.qtpl.go
new file mode 100644
index 0000000..297b5ec
--- /dev/null
+++ b/templates/http_delete.qtpl.go
@@ -0,0 +1,171 @@
+// Code generated by qtc from "http_delete.qtpl". DO NOT EDIT.
+// See https://github.com/valyala/quicktemplate for details.
+
+// This dialog is to be shown to a user when they try to delete a hypha.
+
+//line templates/http_delete.qtpl:2
+package templates
+
+//line templates/http_delete.qtpl:2
+import (
+ qtio422016 "io"
+
+ qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line templates/http_delete.qtpl:2
+var (
+ _ = qtio422016.Copy
+ _ = qt422016.AcquireByteBuffer
+)
+
+//line templates/http_delete.qtpl:2
+func StreamDeleteAskHTML(qw422016 *qt422016.Writer, hyphaName string, isOld bool) {
+//line templates/http_delete.qtpl:2
+ qw422016.N().S(`
+
+
+`)
+//line templates/http_delete.qtpl:13
+ if isOld {
+//line templates/http_delete.qtpl:13
+ qw422016.N().S(`
+
+ Delete `)
+//line templates/http_delete.qtpl:15
+ qw422016.E().S(hyphaName)
+//line templates/http_delete.qtpl:15
+ qw422016.N().S(`?
+ Do you really want to delete hypha `)
+//line templates/http_delete.qtpl:16
+ qw422016.E().S(hyphaName)
+//line templates/http_delete.qtpl:16
+ qw422016.N().S(`?
+ In this version of MycorrhizaWiki you cannot undelete a deleted hypha but the history can still be accessed.
+ Confirm
+ Cancel
+
+`)
+//line templates/http_delete.qtpl:21
+ } else {
+//line templates/http_delete.qtpl:21
+ qw422016.N().S(`
+ `)
+//line templates/http_delete.qtpl:22
+ streamcannotDeleteDueToNonExistence(qw422016, hyphaName)
+//line templates/http_delete.qtpl:22
+ qw422016.N().S(`
+`)
+//line templates/http_delete.qtpl:23
+ }
+//line templates/http_delete.qtpl:23
+ qw422016.N().S(`
+
+`)
+//line templates/http_delete.qtpl:25
+}
+
+//line templates/http_delete.qtpl:25
+func WriteDeleteAskHTML(qq422016 qtio422016.Writer, hyphaName string, isOld bool) {
+//line templates/http_delete.qtpl:25
+ qw422016 := qt422016.AcquireWriter(qq422016)
+//line templates/http_delete.qtpl:25
+ StreamDeleteAskHTML(qw422016, hyphaName, isOld)
+//line templates/http_delete.qtpl:25
+ qt422016.ReleaseWriter(qw422016)
+//line templates/http_delete.qtpl:25
+}
+
+//line templates/http_delete.qtpl:25
+func DeleteAskHTML(hyphaName string, isOld bool) string {
+//line templates/http_delete.qtpl:25
+ qb422016 := qt422016.AcquireByteBuffer()
+//line templates/http_delete.qtpl:25
+ WriteDeleteAskHTML(qb422016, hyphaName, isOld)
+//line templates/http_delete.qtpl:25
+ qs422016 := string(qb422016.B)
+//line templates/http_delete.qtpl:25
+ qt422016.ReleaseByteBuffer(qb422016)
+//line templates/http_delete.qtpl:25
+ return qs422016
+//line templates/http_delete.qtpl:25
+}
+
+//line templates/http_delete.qtpl:27
+func streamcannotDeleteDueToNonExistence(qw422016 *qt422016.Writer, hyphaName string) {
+//line templates/http_delete.qtpl:27
+ qw422016.N().S(`
+
+ Cannot delete `)
+//line templates/http_delete.qtpl:29
+ qw422016.E().S(hyphaName)
+//line templates/http_delete.qtpl:29
+ qw422016.N().S(`
+ This hypha does not exist.
+ Go back
+
+`)
+//line templates/http_delete.qtpl:33
+}
+
+//line templates/http_delete.qtpl:33
+func writecannotDeleteDueToNonExistence(qq422016 qtio422016.Writer, hyphaName string) {
+//line templates/http_delete.qtpl:33
+ qw422016 := qt422016.AcquireWriter(qq422016)
+//line templates/http_delete.qtpl:33
+ streamcannotDeleteDueToNonExistence(qw422016, hyphaName)
+//line templates/http_delete.qtpl:33
+ qt422016.ReleaseWriter(qw422016)
+//line templates/http_delete.qtpl:33
+}
+
+//line templates/http_delete.qtpl:33
+func cannotDeleteDueToNonExistence(hyphaName string) string {
+//line templates/http_delete.qtpl:33
+ qb422016 := qt422016.AcquireByteBuffer()
+//line templates/http_delete.qtpl:33
+ writecannotDeleteDueToNonExistence(qb422016, hyphaName)
+//line templates/http_delete.qtpl:33
+ qs422016 := string(qb422016.B)
+//line templates/http_delete.qtpl:33
+ qt422016.ReleaseByteBuffer(qb422016)
+//line templates/http_delete.qtpl:33
+ return qs422016
+//line templates/http_delete.qtpl:33
+}
diff --git a/templates/http_readers.qtpl b/templates/http_readers.qtpl
index 0c3b4ea..cf85467 100644
--- a/templates/http_readers.qtpl
+++ b/templates/http_readers.qtpl
@@ -6,6 +6,7 @@
Edit
Raw text
History
+ Delete
@@ -32,6 +33,7 @@
Raw text
History
{%s revHash %}
+ Delete
@@ -55,6 +57,7 @@ If `contents` == "", a helpful message is shown instead.
Edit
Raw text
History
+ Delete
diff --git a/templates/http_readers.qtpl.go b/templates/http_readers.qtpl.go
index 97d4a0b..fdccdf4 100644
--- a/templates/http_readers.qtpl.go
+++ b/templates/http_readers.qtpl.go
@@ -40,6 +40,11 @@ func StreamHistoryHTML(qw422016 *qt422016.Writer, hyphaName, tbody string) {
//line templates/http_readers.qtpl:7
qw422016.N().S(`">Raw text
History
+ Delete
@@ -52,193 +57,203 @@ func StreamHistoryHTML(qw422016 *qt422016.Writer, hyphaName, tbody string) {
`)
-//line templates/http_readers.qtpl:20
+//line templates/http_readers.qtpl:21
qw422016.N().S(tbody)
-//line templates/http_readers.qtpl:20
+//line templates/http_readers.qtpl:21
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
}
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
func WriteHistoryHTML(qq422016 qtio422016.Writer, hyphaName, tbody string) {
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
qw422016 := qt422016.AcquireWriter(qq422016)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
StreamHistoryHTML(qw422016, hyphaName, tbody)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
qt422016.ReleaseWriter(qw422016)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
}
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
func HistoryHTML(hyphaName, tbody string) string {
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
qb422016 := qt422016.AcquireByteBuffer()
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
WriteHistoryHTML(qb422016, hyphaName, tbody)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
qs422016 := string(qb422016.B)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
qt422016.ReleaseByteBuffer(qb422016)
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
return qs422016
-//line templates/http_readers.qtpl:24
+//line templates/http_readers.qtpl:25
}
-//line templates/http_readers.qtpl:26
+//line templates/http_readers.qtpl:27
func StreamRevisionHTML(qw422016 *qt422016.Writer, hyphaName, naviTitle, contents, tree, revHash string) {
-//line templates/http_readers.qtpl:26
+//line templates/http_readers.qtpl:27
qw422016.N().S(`
Please note that viewing binary parts of hyphae is not supported in history for now.
`)
-//line templates/http_readers.qtpl:39
+//line templates/http_readers.qtpl:41
qw422016.N().S(naviTitle)
-//line templates/http_readers.qtpl:39
+//line templates/http_readers.qtpl:41
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:40
+//line templates/http_readers.qtpl:42
qw422016.N().S(contents)
-//line templates/http_readers.qtpl:40
+//line templates/http_readers.qtpl:42
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
}
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
func WriteRevisionHTML(qq422016 qtio422016.Writer, hyphaName, naviTitle, contents, tree, revHash string) {
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
qw422016 := qt422016.AcquireWriter(qq422016)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
StreamRevisionHTML(qw422016, hyphaName, naviTitle, contents, tree, revHash)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
qt422016.ReleaseWriter(qw422016)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
}
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
func RevisionHTML(hyphaName, naviTitle, contents, tree, revHash string) string {
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
qb422016 := qt422016.AcquireByteBuffer()
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
WriteRevisionHTML(qb422016, hyphaName, naviTitle, contents, tree, revHash)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
qs422016 := string(qb422016.B)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
qt422016.ReleaseByteBuffer(qb422016)
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
return qs422016
-//line templates/http_readers.qtpl:47
+//line templates/http_readers.qtpl:49
}
// If `contents` == "", a helpful message is shown instead.
-//line templates/http_readers.qtpl:50
+//line templates/http_readers.qtpl:52
func StreamPageHTML(qw422016 *qt422016.Writer, hyphaName, naviTitle, contents, tree string) {
-//line templates/http_readers.qtpl:50
+//line templates/http_readers.qtpl:52
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:61
+//line templates/http_readers.qtpl:64
qw422016.N().S(naviTitle)
-//line templates/http_readers.qtpl:61
+//line templates/http_readers.qtpl:64
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:62
+//line templates/http_readers.qtpl:65
if contents == "" {
-//line templates/http_readers.qtpl:62
+//line templates/http_readers.qtpl:65
qw422016.N().S(`
This hypha has no text. Why not create it?
`)
-//line templates/http_readers.qtpl:64
+//line templates/http_readers.qtpl:67
} else {
-//line templates/http_readers.qtpl:64
+//line templates/http_readers.qtpl:67
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:65
+//line templates/http_readers.qtpl:68
qw422016.N().S(contents)
-//line templates/http_readers.qtpl:65
+//line templates/http_readers.qtpl:68
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:66
+//line templates/http_readers.qtpl:69
}
-//line templates/http_readers.qtpl:66
+//line templates/http_readers.qtpl:69
qw422016.N().S(`
`)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
}
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
func WritePageHTML(qq422016 qtio422016.Writer, hyphaName, naviTitle, contents, tree string) {
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
qw422016 := qt422016.AcquireWriter(qq422016)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
StreamPageHTML(qw422016, hyphaName, naviTitle, contents, tree)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
qt422016.ReleaseWriter(qw422016)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
}
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
func PageHTML(hyphaName, naviTitle, contents, tree string) string {
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
qb422016 := qt422016.AcquireByteBuffer()
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
WritePageHTML(qb422016, hyphaName, naviTitle, contents, tree)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
qs422016 := string(qb422016.B)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
qt422016.ReleaseByteBuffer(qb422016)
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
return qs422016
-//line templates/http_readers.qtpl:81
+//line templates/http_readers.qtpl:84
}