mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-13 05:50:27 +00:00
Sign edits and refactor mutators a little
This commit is contained in:
parent
57751d03f4
commit
b30c368c48
@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
@ -29,7 +30,7 @@ const (
|
||||
type HistoryOp struct {
|
||||
// All errors are appended here.
|
||||
Errs []error
|
||||
opType OpType
|
||||
Type OpType
|
||||
userMsg string
|
||||
name string
|
||||
email string
|
||||
@ -40,7 +41,9 @@ func Operation(opType OpType) *HistoryOp {
|
||||
gitMutex.Lock()
|
||||
hop := &HistoryOp{
|
||||
Errs: []error{},
|
||||
opType: opType,
|
||||
name: "anon",
|
||||
email: "anon@mycorrhiza",
|
||||
Type: opType,
|
||||
}
|
||||
return hop
|
||||
}
|
||||
@ -116,9 +119,11 @@ func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
|
||||
return hop
|
||||
}
|
||||
|
||||
// WithSignature sets a signature for the future commit. You need to pass a username only, the rest is upon us (including email and time).
|
||||
func (hop *HistoryOp) WithSignature(username string) *HistoryOp {
|
||||
hop.name = username
|
||||
hop.email = username + "@mycorrhiza" // A fake email, why not
|
||||
// WithUser sets a user for the commit.
|
||||
func (hop *HistoryOp) WithUser(u *user.User) *HistoryOp {
|
||||
if u.Group != user.UserAnon {
|
||||
hop.name = u.Name
|
||||
hop.email = u.Name + "@mycorrhiza"
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
@ -11,12 +11,14 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/upload-binary/", handlerUploadBinary)
|
||||
http.HandleFunc("/upload-text/", handlerUploadText)
|
||||
// Those that do not actually mutate anything:
|
||||
http.HandleFunc("/edit/", handlerEdit)
|
||||
http.HandleFunc("/delete-ask/", handlerDeleteAsk)
|
||||
http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
|
||||
http.HandleFunc("/rename-ask/", handlerRenameAsk)
|
||||
// And those that do mutate something:
|
||||
http.HandleFunc("/upload-binary/", handlerUploadBinary)
|
||||
http.HandleFunc("/upload-text/", handlerUploadText)
|
||||
http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
|
||||
http.HandleFunc("/rename-confirm/", handlerRenameConfirm)
|
||||
}
|
||||
|
||||
@ -38,20 +40,16 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "rename-confirm")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
_, isOld = HyphaStorage[hyphaName]
|
||||
newName = CanonicalName(rq.PostFormValue("new-name"))
|
||||
_, newNameIsUsed = HyphaStorage[newName]
|
||||
recursive bool
|
||||
recursive = rq.PostFormValue("recursive") == "true"
|
||||
u = user.FromRequest(rq).OrAnon()
|
||||
)
|
||||
if ok := user.CanProceed(rq, "rename-confirm"); !ok {
|
||||
switch {
|
||||
case !u.CanProceed("rename-confirm"):
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
if rq.PostFormValue("recursive") == "true" {
|
||||
recursive = true
|
||||
}
|
||||
switch {
|
||||
case newNameIsUsed:
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists",
|
||||
fmt.Sprintf("Hypha named <a href='/page/%s'>%s</a> already exists.", hyphaName, hyphaName))
|
||||
@ -65,12 +63,12 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: invalid name",
|
||||
"Invalid new name. Names cannot contain characters <code>^?!:#@><*|\"\\'&%</code>")
|
||||
default:
|
||||
if hop := hyphaData.RenameHypha(hyphaName, newName, recursive); len(hop.Errs) == 0 {
|
||||
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
|
||||
} else {
|
||||
if hop := RenameHypha(hyphaName, newName, recursive, u); len(hop.Errs) != 0 {
|
||||
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))
|
||||
} else {
|
||||
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,27 +94,27 @@ func handlerDeleteConfirm(w http.ResponseWriter, rq *http.Request) {
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "delete-confirm")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if ok := user.CanProceed(rq, "delete-confirm"); !ok {
|
||||
if !u.CanProceed("delete-confirm") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
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: <code>%v</code>", hop.Errs))
|
||||
}
|
||||
} 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 delete this hypha because it does not exist.")
|
||||
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
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
||||
@ -152,22 +150,19 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
textData = rq.PostFormValue("text")
|
||||
u = user.FromRequest(rq).OrAnon()
|
||||
)
|
||||
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
|
||||
}
|
||||
if !isOld {
|
||||
hyphaData = &HyphaData{}
|
||||
}
|
||||
if textData == "" {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No text data passed")
|
||||
return
|
||||
}
|
||||
if hop := hyphaData.UploadText(hyphaName, textData, isOld); len(hop.Errs) != 0 {
|
||||
if hop := UploadText(hyphaName, textData, u); len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
||||
} else {
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
@ -177,36 +172,37 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||
// handlerUploadBinary uploads a new binary part for the hypha.
|
||||
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println(rq.URL)
|
||||
hyphaName := HyphaNameFromRq(rq, "upload-binary")
|
||||
if ok := user.CanProceed(rq, "upload-binary"); !ok {
|
||||
var (
|
||||
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if !u.CanProceed("upload-binary") {
|
||||
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to upload attachments.")
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
rq.ParseMultipartForm(10 << 20)
|
||||
|
||||
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
||||
file, handler, err := rq.FormFile("binary")
|
||||
if file != nil {
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
// If file is not passed:
|
||||
if err != nil {
|
||||
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No binary data passed")
|
||||
return
|
||||
}
|
||||
|
||||
// If file is passed:
|
||||
var (
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
mime = handler.Header.Get("Content-Type")
|
||||
hop = UploadBinary(hyphaName, mime, file, u)
|
||||
)
|
||||
if !isOld {
|
||||
hyphaData = &HyphaData{}
|
||||
}
|
||||
hop := hyphaData.UploadBinary(hyphaName, mime, file, isOld)
|
||||
|
||||
if len(hop.Errs) != 0 {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
|
||||
} else {
|
||||
return
|
||||
}
|
||||
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
49
hypha.go
49
hypha.go
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
@ -33,6 +34,15 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist.
|
||||
func GetHyphaData(hyphaName string) (hyphaData *HyphaData, isOld bool) {
|
||||
hyphaData, isOld = HyphaStorage[hyphaName]
|
||||
if hyphaData == nil {
|
||||
hyphaData = &HyphaData{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HyphaData represents a hypha's meta information: binary and text parts rooted paths and content types.
|
||||
type HyphaData struct {
|
||||
textPath string
|
||||
@ -40,10 +50,16 @@ type HyphaData struct {
|
||||
}
|
||||
|
||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
||||
func (hd *HyphaData) uploadHelp(hop *history.HistoryOp, hyphaName, ext string, originalFullPath *string, isOld bool, data []byte) *history.HistoryOp {
|
||||
func uploadHelp(hop *history.HistoryOp, hyphaName, ext string, data []byte, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
hyphaData, isOld = GetHyphaData(hyphaName)
|
||||
fullPath = filepath.Join(WikiDir, hyphaName+ext)
|
||||
originalFullPath = &hyphaData.textPath
|
||||
)
|
||||
if hop.Type == history.TypeEditBinary {
|
||||
originalFullPath = &hyphaData.binaryPath
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
@ -51,30 +67,34 @@ func (hd *HyphaData) uploadHelp(hop *history.HistoryOp, hyphaName, ext string, o
|
||||
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
|
||||
if isOld && *originalFullPath != fullPath && *originalFullPath != "" {
|
||||
if err := history.Rename(*originalFullPath, fullPath); err != nil {
|
||||
return hop.WithError(err)
|
||||
}
|
||||
log.Println("Move", *originalFullPath, "to", fullPath)
|
||||
}
|
||||
// New hyphae must be added to the hypha storage
|
||||
if !isOld {
|
||||
HyphaStorage[hyphaName] = hd
|
||||
HyphaStorage[hyphaName] = hyphaData
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
log.Printf("%v\n", *hd)
|
||||
return hop.WithFiles(fullPath).
|
||||
WithSignature("anon").
|
||||
WithUser(u).
|
||||
Apply()
|
||||
}
|
||||
|
||||
// UploadText loads a new text part from `textData` for hypha `hyphaName` with `hd`. It must be marked if the hypha `isOld`.
|
||||
func (hd *HyphaData) UploadText(hyphaName, textData string, isOld bool) *history.HistoryOp {
|
||||
hop := history.Operation(history.TypeEditText).WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName))
|
||||
return hd.uploadHelp(hop, hyphaName, ".myco", &hd.textPath, isOld, []byte(textData))
|
||||
// UploadText loads a new text part from `textData` for hypha `hyphaName`.
|
||||
func UploadText(hyphaName, textData string, u *user.User) *history.HistoryOp {
|
||||
return uploadHelp(
|
||||
history.
|
||||
Operation(history.TypeEditText).
|
||||
WithMsg(fmt.Sprintf("Edit ‘%s’", hyphaName)),
|
||||
hyphaName, ".myco", []byte(textData), u)
|
||||
}
|
||||
|
||||
// UploadBinary loads a new binary part from `file` for hypha `hyphaName` with `hd`. The contents have the specified `mime` type. It must be marked if the hypha `isOld`.
|
||||
func (hd *HyphaData) UploadBinary(hyphaName, mime string, file multipart.File, isOld bool) *history.HistoryOp {
|
||||
func UploadBinary(hyphaName, mime string, file multipart.File, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", hyphaName, mime))
|
||||
data, err = ioutil.ReadAll(file)
|
||||
@ -82,16 +102,15 @@ func (hd *HyphaData) UploadBinary(hyphaName, mime string, file multipart.File, i
|
||||
if err != nil {
|
||||
return hop.WithError(err).Apply()
|
||||
}
|
||||
|
||||
return hd.uploadHelp(hop, hyphaName, MimeToExtension(mime), &hd.binaryPath, isOld, data)
|
||||
return uploadHelp(hop, hyphaName, MimeToExtension(mime), data, u)
|
||||
}
|
||||
|
||||
// DeleteHypha deletes hypha and makes a history record about that.
|
||||
func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp {
|
||||
func (hd *HyphaData) DeleteHypha(hyphaName string, u *user.User) *history.HistoryOp {
|
||||
hop := history.Operation(history.TypeDeleteHypha).
|
||||
WithFilesRemoved(hd.textPath, hd.binaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", hyphaName)).
|
||||
WithSignature("anon").
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
delete(HyphaStorage, hyphaName)
|
||||
@ -138,7 +157,7 @@ func relocateHyphaData(hyphaNames []string, replaceName func(string) string) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func RenameHypha(hyphaName, newName string, recursive bool, u *user.User) *history.HistoryOp {
|
||||
var (
|
||||
replaceName = func(str string) string {
|
||||
return strings.Replace(str, hyphaName, newName, 1)
|
||||
@ -157,7 +176,7 @@ func (hd *HyphaData) RenameHypha(hyphaName, newName string, recursive bool) *his
|
||||
}
|
||||
hop.WithFilesRenamed(renameMap).
|
||||
WithMsg(fmt.Sprintf(renameMsg, hyphaName, newName)).
|
||||
WithSignature("anon").
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
relocateHyphaData(hyphaNames, replaceName)
|
||||
|
@ -62,9 +62,9 @@ func (ug UserGroup) CanAccessRoute(route string) bool {
|
||||
}
|
||||
|
||||
func CanProceed(rq *http.Request, route string) bool {
|
||||
ug := UserAnon
|
||||
if u := FromRequest(rq); u != nil {
|
||||
ug = u.Group
|
||||
return FromRequest(rq).OrAnon().CanProceed(route)
|
||||
}
|
||||
return ug.CanAccessRoute(route)
|
||||
|
||||
func (u *User) CanProceed(route string) bool {
|
||||
return u.Group.CanAccessRoute(route)
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func FromRequest(rq *http.Request) *User {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return UserStorage.userByToken(cookie.Value)
|
||||
return UserStorage.userByToken(cookie.Value).OrAnon()
|
||||
}
|
||||
|
||||
func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string {
|
||||
|
Loading…
Reference in New Issue
Block a user