From 002e9f7a9320877a4c0e43d10a86f2da29caf2b1 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Fri, 4 Feb 2022 22:04:39 +0500 Subject: [PATCH] Delete uploadHelp, refactor UploadText --- hyphae/empty_hypha.go | 11 +--- hyphae/interface.go | 2 +- shroom/can.go | 2 + shroom/upload.go | 127 ++++++++++++++++++++++++++++++------------ shroom/view.go | 5 ++ 5 files changed, 103 insertions(+), 44 deletions(-) diff --git a/hyphae/empty_hypha.go b/hyphae/empty_hypha.go index f25e735..21c0b4b 100644 --- a/hyphae/empty_hypha.go +++ b/hyphae/empty_hypha.go @@ -12,10 +12,6 @@ func (e *EmptyHypha) CanonicalName() string { return e.canonicalName } -func (e *EmptyHypha) DoesExist() bool { - return false -} - func (e *EmptyHypha) HasTextPart() bool { return false } @@ -31,10 +27,9 @@ func NewEmptyHypha(hyphaName string) *EmptyHypha { } } -func FillEmptyHyphaUpToMediaHypha(e *EmptyHypha) *MediaHypha { // sic! +func FillEmptyHyphaUpToTextualHypha(e *EmptyHypha, textPath string) *MediaHypha { // sic! return &MediaHypha{ - name: e.CanonicalName(), - TextPath: "", - binaryPath: "", + name: e.CanonicalName(), + TextPath: textPath, } } diff --git a/hyphae/interface.go b/hyphae/interface.go index c4ff0bd..ae8e991 100644 --- a/hyphae/interface.go +++ b/hyphae/interface.go @@ -85,7 +85,7 @@ func InsertIfNew(h Hypher) (madeNewRecord bool) { } } -// ByName returns a hypha by name. It may have been recorded to the storage. +// ByName returns a hypha by name. It returns an *EmptyHypha if there is no such hypha. This function is the only source of empty hyphae. func ByName(hyphaName string) (h Hypher) { byNamesMutex.Lock() defer byNamesMutex.Unlock() diff --git a/shroom/can.go b/shroom/can.go index 762e915..e5912c3 100644 --- a/shroom/can.go +++ b/shroom/can.go @@ -8,6 +8,8 @@ import ( "github.com/bouncepaw/mycorrhiza/user" ) +// TODO: get rid of this abomination + func canFactory( rejectLogger func(hyphae.Hypher, *user.User, string), action string, diff --git a/shroom/upload.go b/shroom/upload.go index 1cc37b2..db29bb7 100644 --- a/shroom/upload.go +++ b/shroom/upload.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bouncepaw/mycorrhiza/hyphae/backlinks" + "github.com/bouncepaw/mycorrhiza/mimetype" "io" "log" "mime/multipart" @@ -16,44 +17,115 @@ import ( "github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/hyphae" "github.com/bouncepaw/mycorrhiza/l18n" - "github.com/bouncepaw/mycorrhiza/mimetype" "github.com/bouncepaw/mycorrhiza/user" ) -// UploadText edits a hypha' text part and makes a history record about that. -func UploadText(h hyphae.Hypher, data []byte, message string, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) { - hop = history.Operation(history.TypeEditText) - - var action string +func historyMessageForTextUpload(h hyphae.Hypher, userMessage string) string { + var verb string switch h.(type) { case *hyphae.EmptyHypha: - action = "Create" + verb = "Create" default: - action = "Edit" + verb = "Edit" } - if message == "" { - hop.WithMsg(fmt.Sprintf("%s ‘%s’", action, h.CanonicalName())) - } else { - hop.WithMsg(fmt.Sprintf("%s ‘%s’: %s", action, h.CanonicalName(), message)) + if userMessage == "" { + return fmt.Sprintf("%s ‘%s’", verb, h.CanonicalName()) + } + return fmt.Sprintf("%s ‘%s’: %s", verb, h.CanonicalName(), userMessage) +} + +func writeTextToDiskForEmptyHypha(eh *hyphae.EmptyHypha, data []byte) error { + h := hyphae.FillEmptyHyphaUpToTextualHypha(eh, filepath.Join(files.HyphaeDir(), eh.CanonicalName()+".myco")) + + return writeTextToDiskForNonEmptyHypha(h, data) +} + +func writeTextToDiskForNonEmptyHypha(h *hyphae.MediaHypha, data []byte) error { + if err := os.MkdirAll(filepath.Dir(h.TextPartPath()), 0777); err != nil { + return err } - if errtitle, err := CanEdit(u, h, lc); err != nil { - return hop.WithErrAbort(err), errtitle + if err := os.WriteFile(h.TextPartPath(), data, 0666); err != nil { + return err } - if len(bytes.TrimSpace(data)) == 0 { + return nil +} + +// UploadText edits the hypha's text part and makes a history record about that. +func UploadText(h hyphae.Hypher, data []byte, userMessage string, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) { + hop = history. + Operation(history.TypeEditText). + WithMsg(historyMessageForTextUpload(h, userMessage)) + + // Privilege check + if !u.CanProceed("upload-text") { + rejectEditLog(h, u, "no rights") + return hop.WithErrAbort(errors.New(lc.Get("ui.act_norights_edit"))), lc.Get("ui.act_no_rights") + } + + // Hypha name exploit check + if !hyphae.IsValidName(h.CanonicalName()) { + // We check for the name only. I suppose the filepath would be valid as well. + err := errors.New("invalid hypha name") + return hop.WithErrAbort(err), err.Error() + } + + // Empty data check + if len(bytes.TrimSpace(data)) == 0 { // if nothing but whitespace switch h := h.(type) { + case *hyphae.EmptyHypha: + // It's ok, just like cancel button. + return hop.Abort(), "" case *hyphae.MediaHypha: - if h.Kind() != hyphae.HyphaMedia { + switch h.Kind() { + case hyphae.HyphaMedia: + // Writing no description, it's ok, just like cancel button. + return hop.Abort(), "" + case hyphae.HyphaText: + // What do you want passing nothing for a textual hypha? return hop.WithErrAbort(errors.New("No data passed")), "Empty" } } } - return uploadHelp(h, hop, ".myco", data, u) + // At this point, we have a savable user-generated Mycomarkup document. Gotta save it. + + switch h := h.(type) { + case *hyphae.EmptyHypha: + err := writeTextToDiskForEmptyHypha(h, data) + if err != nil { + return hop.WithErrAbort(err), err.Error() + } + + hyphae.InsertIfNew(h) + case *hyphae.MediaHypha: + oldText, err := FetchTextPart(h) + if err != nil { + return hop.WithErrAbort(err), err.Error() + } + + // TODO: that []byte(...) part should be removed + if bytes.Compare(data, []byte(oldText)) == 0 { + // No changes! Just like cancel button + return hop.Abort(), "" + } + + err = writeTextToDiskForNonEmptyHypha(h, data) + if err != nil { + return hop.WithErrAbort(err), err.Error() + } + + backlinks.UpdateBacklinksAfterEdit(h, oldText) + } + + return hop. + WithFiles(h.TextPartPath()). + WithUser(u). + Apply(), "" } -// UploadBinary edits a hypha' attachment and makes a history record about that. +// UploadBinary edits the hypha's media part and makes a history record about that. func UploadBinary(h hyphae.Hypher, mime string, file multipart.File, u *user.User, lc *l18n.Localizer) (*history.Op, string) { var ( hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload attachment for ‘%s’ with type ‘%s’", h.CanonicalName(), mime)) @@ -70,15 +142,11 @@ func UploadBinary(h hyphae.Hypher, mime string, file multipart.File, u *user.Use return hop.WithErrAbort(errors.New("No data passed")), "Empty" } - return uploadHelp(h, hop, mimetype.ToExtension(mime), data, u) -} + ext := mimetype.ToExtension(mime) -// uploadHelp is a helper function for UploadText and UploadBinary -func uploadHelp(h hyphae.Hypher, hop *history.Op, ext string, data []byte, u *user.User) (*history.Op, string) { var ( fullPath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+ext) sourceFullPath = h.TextPartPath() - originalText = "" // for backlink update ) if !isValidPath(fullPath) || !hyphae.IsValidName(h.CanonicalName()) { err := errors.New("bad path") @@ -92,10 +160,6 @@ func uploadHelp(h hyphae.Hypher, hop *history.Op, ext string, data []byte, u *us return hop.WithErrAbort(err), err.Error() } - if hop.Type == history.TypeEditText { - originalText, _ = FetchTextPart(h) - } - if err := os.WriteFile(fullPath, data, 0666); err != nil { return hop.WithErrAbort(err), err.Error() } @@ -122,14 +186,7 @@ func uploadHelp(h hyphae.Hypher, hop *history.Op, ext string, data []byte, u *us } // sic! - if h := h.(*hyphae.MediaHypha); hop.Type == history.TypeEditBinary { - h.SetBinaryPath(fullPath) - } else { - h.TextPath = fullPath - } - if hop.Type == history.TypeEditText { - backlinks.UpdateBacklinksAfterEdit(h, originalText) - } + h.(*hyphae.MediaHypha).SetBinaryPath(fullPath) return hop.WithFiles(fullPath).WithUser(u).Apply(), "" } diff --git a/shroom/view.go b/shroom/view.go index d770dc9..b410dc5 100644 --- a/shroom/view.go +++ b/shroom/view.go @@ -1,6 +1,7 @@ package shroom import ( + "errors" "os" "github.com/bouncepaw/mycorrhiza/cfg" @@ -9,6 +10,10 @@ import ( // FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned. func FetchTextPart(h hyphae.Hypher) (string, error) { + switch h.(type) { + case *hyphae.EmptyHypha: + return "", errors.New("empty hyphae have no text") + } if !h.HasTextPart() { return "", nil }