2020-08-05 15:08:59 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2020-08-10 19:58:02 +00:00
|
|
|
|
2021-01-16 16:42:18 +00:00
|
|
|
"github.com/bouncepaw/mycorrhiza/markup"
|
2020-08-31 17:52:26 +00:00
|
|
|
"github.com/bouncepaw/mycorrhiza/templates"
|
2020-11-15 12:58:13 +00:00
|
|
|
"github.com/bouncepaw/mycorrhiza/user"
|
2020-08-31 17:52:26 +00:00
|
|
|
"github.com/bouncepaw/mycorrhiza/util"
|
2020-08-05 15:08:59 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2020-11-18 13:07:53 +00:00
|
|
|
// Those that do not actually mutate anything:
|
2020-08-05 15:08:59 +00:00
|
|
|
http.HandleFunc("/edit/", handlerEdit)
|
2020-09-29 15:04:22 +00:00
|
|
|
http.HandleFunc("/delete-ask/", handlerDeleteAsk)
|
2020-10-02 15:31:59 +00:00
|
|
|
http.HandleFunc("/rename-ask/", handlerRenameAsk)
|
2021-01-19 18:08:59 +00:00
|
|
|
http.HandleFunc("/unattach-ask/", handlerUnattachAsk)
|
2020-11-18 13:07:53 +00:00
|
|
|
// And those that do mutate something:
|
|
|
|
http.HandleFunc("/upload-binary/", handlerUploadBinary)
|
|
|
|
http.HandleFunc("/upload-text/", handlerUploadText)
|
|
|
|
http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
|
2020-10-02 15:31:59 +00:00
|
|
|
http.HandleFunc("/rename-confirm/", handlerRenameConfirm)
|
2021-01-19 18:08:59 +00:00
|
|
|
http.HandleFunc("/unattach-confirm/", handlerUnattachConfirm)
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlerUnattachAsk(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "unattach-ask")
|
|
|
|
hd, isOld = HyphaStorage[hyphaName]
|
|
|
|
hasAmnt = hd != nil && hd.binaryPath != ""
|
|
|
|
)
|
|
|
|
if !hasAmnt {
|
|
|
|
HttpErr(w, http.StatusBadRequest, hyphaName, "Cannot unattach", "No attachment attached yet, therefore you cannot unattach")
|
|
|
|
log.Println("Rejected (no amnt):", rq.URL)
|
|
|
|
return
|
|
|
|
} else if ok := user.CanProceed(rq, "unattach-confirm"); !ok {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to unattach attachments")
|
|
|
|
log.Println("Rejected (no rights):", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
util.HTTP200Page(w, base("Unattach "+hyphaName+"?", templates.UnattachAskHTML(rq, hyphaName, isOld)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlerUnattachConfirm(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "unattach-confirm")
|
|
|
|
hyphaData, isOld = HyphaStorage[hyphaName]
|
|
|
|
hasAmnt = hyphaData != nil && hyphaData.binaryPath != ""
|
|
|
|
u = user.FromRequest(rq)
|
|
|
|
)
|
|
|
|
if !u.CanProceed("unattach-confirm") {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to unattach attachments")
|
|
|
|
log.Println("Rejected (no rights):", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !hasAmnt {
|
|
|
|
HttpErr(w, http.StatusBadRequest, hyphaName, "Cannot unattach", "No attachment attached yet, therefore you cannot unattach")
|
|
|
|
log.Println("Rejected (no amnt):", rq.URL)
|
|
|
|
return
|
|
|
|
} else if !isOld {
|
|
|
|
// The precondition is to have the hypha in the first place.
|
|
|
|
HttpErr(w, http.StatusPreconditionFailed, hyphaName,
|
|
|
|
"Error: no such hypha",
|
|
|
|
"Could not unattach this hypha because it does not exist")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if hop := hyphaData.UnattachHypha(hyphaName, u); len(hop.Errs) != 0 {
|
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
|
|
|
"Error: could not unattach hypha",
|
|
|
|
fmt.Sprintf("Could not unattach this hypha due to internal errors. Server errors: <code>%v</code>", hop.Errs))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
2020-10-02 15:31:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func handlerRenameAsk(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "rename-ask")
|
|
|
|
_, isOld = HyphaStorage[hyphaName]
|
|
|
|
)
|
2020-11-15 12:58:13 +00:00
|
|
|
if ok := user.CanProceed(rq, "rename-confirm"); !ok {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-11-16 15:26:03 +00:00
|
|
|
util.HTTP200Page(w, base("Rename "+hyphaName+"?", templates.RenameAskHTML(rq, hyphaName, isOld)))
|
2020-10-02 15:31:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "rename-confirm")
|
2020-11-18 13:07:53 +00:00
|
|
|
_, isOld = HyphaStorage[hyphaName]
|
2020-10-02 15:31:59 +00:00
|
|
|
newName = CanonicalName(rq.PostFormValue("new-name"))
|
|
|
|
_, newNameIsUsed = HyphaStorage[newName]
|
2020-11-18 13:07:53 +00:00
|
|
|
recursive = rq.PostFormValue("recursive") == "true"
|
2021-01-09 20:49:48 +00:00
|
|
|
u = user.FromRequest(rq)
|
2020-10-02 15:31:59 +00:00
|
|
|
)
|
2020-11-18 13:07:53 +00:00
|
|
|
switch {
|
|
|
|
case !u.CanProceed("rename-confirm"):
|
2020-11-15 12:58:13 +00:00
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
2020-10-02 15:31:59 +00:00
|
|
|
case newNameIsUsed:
|
|
|
|
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists",
|
|
|
|
fmt.Sprintf("Hypha named <a href='/page/%s'>%s</a> 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 <code>^?!:#@><*|\"\\'&%</code>")
|
|
|
|
default:
|
2020-11-18 13:07:53 +00:00
|
|
|
if hop := RenameHypha(hyphaName, newName, recursive, u); len(hop.Errs) != 0 {
|
2020-10-02 15:31:59 +00:00
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
|
|
|
"Error: could not rename hypha",
|
|
|
|
fmt.Sprintf("Could not rename this hypha due to an internal error. Server errors: <code>%v</code>", hop.Errs))
|
2020-11-18 13:07:53 +00:00
|
|
|
} else {
|
|
|
|
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
|
2020-10-02 15:31:59 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-29 15:04:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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]
|
|
|
|
)
|
2020-11-15 12:58:13 +00:00
|
|
|
if ok := user.CanProceed(rq, "delete-ask"); !ok {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-11-16 15:26:03 +00:00
|
|
|
util.HTTP200Page(w, base("Delete "+hyphaName+"?", templates.DeleteAskHTML(rq, hyphaName, isOld)))
|
2020-09-29 15:04:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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]
|
2020-11-18 13:07:53 +00:00
|
|
|
u = user.FromRequest(rq)
|
2020-09-29 15:04:22 +00:00
|
|
|
)
|
2021-01-02 21:25:04 +00:00
|
|
|
if !user.CanProceed(rq, "delete-confirm") {
|
2020-11-15 12:58:13 +00:00
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-11-18 13:07:53 +00:00
|
|
|
if !isOld {
|
2020-09-29 15:04:22 +00:00
|
|
|
// 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.")
|
2020-11-18 13:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if hop := hyphaData.DeleteHypha(hyphaName, u); len(hop.Errs) != 0 {
|
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
|
|
|
"Error: could not delete hypha",
|
|
|
|
fmt.Sprintf("Could not delete this hypha due to internal errors. Server errors: <code>%v</code>", hop.Errs))
|
|
|
|
return
|
2020-09-29 15:04:22 +00:00
|
|
|
}
|
2020-11-18 13:07:53 +00:00
|
|
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
2020-08-05 15:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
|
|
|
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "edit")
|
|
|
|
hyphaData, isOld = HyphaStorage[hyphaName]
|
|
|
|
warning string
|
|
|
|
textAreaFill string
|
|
|
|
err error
|
|
|
|
)
|
2020-11-15 12:58:13 +00:00
|
|
|
if ok := user.CanProceed(rq, "edit"); !ok {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to edit pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-08-05 15:08:59 +00:00
|
|
|
if isOld {
|
|
|
|
textAreaFill, err = FetchTextPart(hyphaData)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
2020-10-22 16:42:48 +00:00
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", "Could not fetch text data")
|
2020-08-05 15:08:59 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warning = `<p>You are creating a new hypha.</p>`
|
|
|
|
}
|
2020-11-16 15:26:03 +00:00
|
|
|
util.HTTP200Page(w, base("Edit "+hyphaName, templates.EditHTML(rq, hyphaName, textAreaFill, warning)))
|
2020-08-05 15:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// handlerUploadText uploads a new text part for the hypha.
|
|
|
|
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
|
|
|
var (
|
2020-11-18 13:07:53 +00:00
|
|
|
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
|
|
|
textData = rq.PostFormValue("text")
|
2021-01-16 16:42:18 +00:00
|
|
|
action = rq.PostFormValue("action")
|
2021-01-09 20:49:48 +00:00
|
|
|
u = user.FromRequest(rq)
|
2020-08-05 15:08:59 +00:00
|
|
|
)
|
2020-11-15 12:58:13 +00:00
|
|
|
if ok := user.CanProceed(rq, "upload-text"); !ok {
|
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to edit pages.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-08-05 15:08:59 +00:00
|
|
|
if textData == "" {
|
2020-10-22 16:42:48 +00:00
|
|
|
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No text data passed")
|
2020-08-05 15:08:59 +00:00
|
|
|
return
|
|
|
|
}
|
2021-01-16 16:42:18 +00:00
|
|
|
if action == "Preview" {
|
|
|
|
util.HTTP200Page(w, base("Preview "+hyphaName, templates.PreviewHTML(rq, hyphaName, textData, "", markup.Doc(hyphaName, textData).AsHTML())))
|
|
|
|
} else if hop := UploadText(hyphaName, textData, u); len(hop.Errs) != 0 {
|
2020-10-22 16:42:48 +00:00
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
2020-08-05 15:08:59 +00:00
|
|
|
} else {
|
2020-10-22 16:42:48 +00:00
|
|
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
2020-08-05 15:08:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handlerUploadBinary uploads a new binary part for the hypha.
|
|
|
|
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
log.Println(rq.URL)
|
2020-11-18 13:07:53 +00:00
|
|
|
var (
|
|
|
|
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
2021-01-09 20:49:48 +00:00
|
|
|
u = user.FromRequest(rq)
|
2020-11-18 13:07:53 +00:00
|
|
|
)
|
|
|
|
if !u.CanProceed("upload-binary") {
|
2020-11-15 12:58:13 +00:00
|
|
|
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to upload attachments.")
|
|
|
|
log.Println("Rejected", rq.URL)
|
|
|
|
return
|
|
|
|
}
|
2020-10-22 16:42:48 +00:00
|
|
|
|
2020-11-18 13:07:53 +00:00
|
|
|
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
2020-08-05 15:08:59 +00:00
|
|
|
file, handler, err := rq.FormFile("binary")
|
|
|
|
if file != nil {
|
|
|
|
defer file.Close()
|
|
|
|
}
|
2020-11-18 13:07:53 +00:00
|
|
|
|
2020-08-05 15:08:59 +00:00
|
|
|
// If file is not passed:
|
|
|
|
if err != nil {
|
2020-10-22 16:42:48 +00:00
|
|
|
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No binary data passed")
|
2020-08-05 15:08:59 +00:00
|
|
|
return
|
|
|
|
}
|
2020-11-18 13:07:53 +00:00
|
|
|
|
2020-08-05 15:08:59 +00:00
|
|
|
// If file is passed:
|
|
|
|
var (
|
2020-11-18 13:07:53 +00:00
|
|
|
mime = handler.Header.Get("Content-Type")
|
|
|
|
hop = UploadBinary(hyphaName, mime, file, u)
|
2020-08-05 15:08:59 +00:00
|
|
|
)
|
|
|
|
|
2020-10-22 16:42:48 +00:00
|
|
|
if len(hop.Errs) != 0 {
|
|
|
|
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
2020-11-18 13:07:53 +00:00
|
|
|
return
|
2020-08-19 18:54:23 +00:00
|
|
|
}
|
2020-11-18 13:07:53 +00:00
|
|
|
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
2020-08-05 15:08:59 +00:00
|
|
|
}
|