1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-10-30 03:36:16 +00:00

Break a lot of stuff

Starring:
* Broken error localization for now (got in the way)
* The title for error pages is the same for all errors (who cares anyway)
* New bugs
* The brand new /rename/ handler
This commit is contained in:
Timur Ismagilov 2022-02-19 19:42:32 +03:00
parent dd3f2c698a
commit eb9acb718e
18 changed files with 219 additions and 241 deletions

View File

@ -2,6 +2,7 @@ package hyphae
import "sync" import "sync"
// EmptyHypha is a hypha that does not exist and is not stored anywhere. You get one when querying for a hypha that was not created before.
type EmptyHypha struct { type EmptyHypha struct {
sync.RWMutex sync.RWMutex
@ -12,6 +13,7 @@ func (e *EmptyHypha) CanonicalName() string {
return e.canonicalName return e.canonicalName
} }
// ExtendEmptyToTextual returns a new textual hypha with the same name as the given empty hypha. The created hypha is not stored yet.
func ExtendEmptyToTextual(e *EmptyHypha, mycoFilePath string) *TextualHypha { func ExtendEmptyToTextual(e *EmptyHypha, mycoFilePath string) *TextualHypha {
return &TextualHypha{ return &TextualHypha{
canonicalName: e.CanonicalName(), canonicalName: e.CanonicalName(),
@ -19,6 +21,7 @@ func ExtendEmptyToTextual(e *EmptyHypha, mycoFilePath string) *TextualHypha {
} }
} }
// ExtendEmptyToMedia returns a new media hypha with the same name as the given empty hypha. The created hypha is not stored yet.
func ExtendEmptyToMedia(e *EmptyHypha, mediaFilePath string) *MediaHypha { func ExtendEmptyToMedia(e *EmptyHypha, mediaFilePath string) *MediaHypha {
return &MediaHypha{ return &MediaHypha{
canonicalName: e.CanonicalName(), canonicalName: e.CanonicalName(),

View File

@ -4,20 +4,17 @@ import (
"github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/util"
) )
// ExistingHypha is not EmptyHypha. // ExistingHypha is not EmptyHypha. *MediaHypha and *TextualHypha implement this interface.
type ExistingHypha interface { type ExistingHypha interface {
Hypha Hypha
// DoesExist does nothing except marks that the type is an ExistingHypha.
DoesExist()
HasTextFile() bool HasTextFile() bool
TextFilePath() string TextFilePath() string
} }
// RenameHyphaTo renames a hypha and renames stored filepaths as needed. The actual files are not moved, move them yourself. // RenameHyphaTo renames a hypha and renames stored filepaths as needed. The actual files are not moved, move them yourself.
func RenameHyphaTo(h ExistingHypha, newName string, replaceName func(string) string) { func RenameHyphaTo(h ExistingHypha, newName string, replaceName func(string) string) {
// TODO: that replaceName is suspicious. // TODO: that replaceName is suspicious, get rid of it.
newName = util.CanonicalName(newName) newName = util.CanonicalName(newName)
byNamesMutex.Lock() byNamesMutex.Lock()
h.Lock() h.Lock()
@ -37,3 +34,28 @@ func RenameHyphaTo(h ExistingHypha, newName string, replaceName func(string) str
byNamesMutex.Unlock() byNamesMutex.Unlock()
h.Unlock() h.Unlock()
} }
// DeleteHypha deletes the hypha from the storage.
func DeleteHypha(h ExistingHypha) {
byNamesMutex.Lock()
h.Lock()
delete(byNames, h.CanonicalName())
decrementCount()
byNamesMutex.Unlock()
h.Unlock()
}
// Insert inserts the hypha into the storage, possibly overwriting the previous hypha with the same name. Count incrementation is done if needed. You cannot insert an empty hypha.
func Insert(h ExistingHypha) (madeNewRecord bool) {
_, recorded := byNames[h.CanonicalName()]
byNamesMutex.Lock()
byNames[h.CanonicalName()] = h
byNamesMutex.Unlock()
if !recorded {
incrementCount()
}
return !recorded
}

View File

@ -6,12 +6,12 @@ import (
"sync" "sync"
) )
// HyphaPattern is a pattern which all hyphae names must match. // hyphaNamePattern is a pattern which all hyphae names must match.
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`) var hyphaNamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
// IsValidName checks for invalid characters and path traversals. // IsValidName checks for invalid characters and path traversals.
func IsValidName(hyphaName string) bool { func IsValidName(hyphaName string) bool {
if !HyphaPattern.MatchString(hyphaName) { if !hyphaNamePattern.MatchString(hyphaName) {
return false return false
} }
for _, segment := range strings.Split(hyphaName, "/") { for _, segment := range strings.Split(hyphaName, "/") {
@ -22,38 +22,16 @@ func IsValidName(hyphaName string) bool {
return true return true
} }
// Hypha is a hypha you know and love. // Hypha is the hypha you know and love.
type Hypha interface { type Hypha interface {
sync.Locker sync.Locker
// CanonicalName returns the canonical name of the hypha.
//
// util.CanonicalName(h.CanonicalName()) == h.CanonicalName()
CanonicalName() string CanonicalName() string
} }
// DeleteHypha deletes the hypha from the storage.
func DeleteHypha(h ExistingHypha) {
byNamesMutex.Lock()
h.Lock()
delete(byNames, h.CanonicalName())
decrementCount()
byNamesMutex.Unlock()
h.Unlock()
}
// Insert inserts the hypha into the storage, possibly overwriting the previous hypha with the same name. Count incrementation is done if needed. You cannot insert an empty hypha.
func Insert(h ExistingHypha) (madeNewRecord bool) {
_, recorded := byNames[h.CanonicalName()]
byNamesMutex.Lock()
byNames[h.CanonicalName()] = h
byNamesMutex.Unlock()
if !recorded {
incrementCount()
}
return !recorded
}
// 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. // 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 Hypha) { func ByName(hyphaName string) (h Hypha) {
byNamesMutex.Lock() byNamesMutex.Lock()

View File

@ -14,9 +14,6 @@ type MediaHypha struct {
mediaFilePath string mediaFilePath string
} }
func (m *MediaHypha) DoesExist() {
}
func (m *MediaHypha) CanonicalName() string { func (m *MediaHypha) CanonicalName() string {
return m.canonicalName return m.canonicalName
} }

View File

@ -4,6 +4,7 @@ import (
"sync" "sync"
) )
// TextualHypha is a hypha with text, and nothing else. An article, a note, a poem, whatnot.
type TextualHypha struct { type TextualHypha struct {
sync.RWMutex sync.RWMutex
@ -11,9 +12,6 @@ type TextualHypha struct {
mycoFilePath string mycoFilePath string
} }
func (t *TextualHypha) DoesExist() {
}
func (t *TextualHypha) CanonicalName() string { func (t *TextualHypha) CanonicalName() string {
return t.canonicalName return t.canonicalName
} }
@ -26,6 +24,7 @@ func (t *TextualHypha) TextFilePath() string {
return t.mycoFilePath return t.mycoFilePath
} }
// ExtendTextualToMedia returns a new media hypha with the same name and text file as the given textual hypha. The new hypha is not stored yet.
func ExtendTextualToMedia(t *TextualHypha, mediaFilePath string) *MediaHypha { func ExtendTextualToMedia(t *TextualHypha, mediaFilePath string) *MediaHypha {
return &MediaHypha{ return &MediaHypha{
canonicalName: t.CanonicalName(), canonicalName: t.CanonicalName(),

View File

@ -17,29 +17,29 @@ func canFactory(
noRightsMsg string, noRightsMsg string,
notExistsMsg string, notExistsMsg string,
mustExist bool, mustExist bool,
) func(*user.User, hyphae.Hypha, *l18n.Localizer) (string, error) { ) func(*user.User, hyphae.Hypha, *l18n.Localizer) error {
return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (string, error) { return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
if !u.CanProceed(action) { if !u.CanProceed(action) {
rejectLogger(h, u, "no rights") rejectLogger(h, u, "no rights")
return lc.Get("ui.act_no_rights"), errors.New(lc.Get(noRightsMsg)) return errors.New(noRightsMsg)
} }
if mustExist { if mustExist {
switch h.(type) { switch h.(type) {
case *hyphae.EmptyHypha: case *hyphae.EmptyHypha:
rejectLogger(h, u, "does not exist") rejectLogger(h, u, "does not exist")
return lc.Get("ui.act_notexist"), errors.New(lc.Get(notExistsMsg)) return errors.New(notExistsMsg)
} }
} }
if dispatcher == nil { if dispatcher == nil {
return "", nil return nil
} }
errmsg, errtitle := dispatcher(h, u, lc) errmsg, errtitle := dispatcher(h, u, lc)
if errtitle == "" { if errtitle == "" {
return "", nil return nil
} }
return errtitle, errors.New(errmsg) return errors.New(errmsg)
} }
} }
@ -54,22 +54,12 @@ var (
true, true,
) )
CanRename = canFactory(
rejectRenameLog,
"rename-confirm",
nil,
"ui.act_norights_rename",
"ui.act_notexist_rename",
true,
)
CanUnattach = canFactory( CanUnattach = canFactory(
rejectUnattachLog, rejectUnattachLog,
"unattach-confirm", "unattach-confirm",
func(h hyphae.Hypha, u *user.User, lc *l18n.Localizer) (errmsg, errtitle string) { func(h hyphae.Hypha, u *user.User, lc *l18n.Localizer) (errmsg, errtitle string) {
switch h := h.(type) { switch h := h.(type) {
case *hyphae.EmptyHypha: case *hyphae.EmptyHypha, *hyphae.TextualHypha:
case *hyphae.TextualHypha:
rejectUnattachLog(h, u, "no amnt") rejectUnattachLog(h, u, "no amnt")
return lc.Get("ui.act_noattachment_tip"), lc.Get("ui.act_noattachment") return lc.Get("ui.act_noattachment_tip"), lc.Get("ui.act_noattachment")
} }

View File

@ -11,17 +11,16 @@ import (
) )
// DeleteHypha deletes hypha and makes a history record about that. // DeleteHypha deletes hypha and makes a history record about that.
func DeleteHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *history.Op, errtitle string) { func DeleteHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
hop = history. if err := CanDelete(u, h, lc); err != nil {
return err
}
hop := history.
Operation(history.TypeDeleteHypha). Operation(history.TypeDeleteHypha).
WithMsg(fmt.Sprintf("Delete %s", h.CanonicalName())). WithMsg(fmt.Sprintf("Delete %s", h.CanonicalName())).
WithUser(u) WithUser(u)
if errtitle, err := CanDelete(u, h, lc); errtitle != "" {
hop.WithErrAbort(err)
return hop, errtitle
}
switch h := h.(type) { switch h := h.(type) {
case *hyphae.MediaHypha: case *hyphae.MediaHypha:
hop.WithFilesRemoved(h.MediaFilePath(), h.TextFilePath()) hop.WithFilesRemoved(h.MediaFilePath(), h.TextFilePath())
@ -36,5 +35,5 @@ func DeleteHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *history
backlinks.UpdateBacklinksAfterDelete(h, originalText) backlinks.UpdateBacklinksAfterDelete(h, originalText)
hyphae.DeleteHypha(h.(hyphae.ExistingHypha)) // we panicked before, so it's safe hyphae.DeleteHypha(h.(hyphae.ExistingHypha)) // we panicked before, so it's safe
} }
return hop, "" return nil
} }

View File

@ -8,76 +8,68 @@ import (
"github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae" "github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user" "github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/util"
) )
func canRenameThisToThat(oh hyphae.Hypha, nh hyphae.Hypha, u *user.User, lc *l18n.Localizer) (errtitle string, err error) { // Rename renames the old hypha to the new name. Call if and only if the user has the permission to rename.
switch nh.(type) { func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, u *user.User) error {
case *hyphae.EmptyHypha: if newName == "" {
default: rejectRenameLog(oldHypha, u, "no new name given")
rejectRenameLog(oh, u, fmt.Sprintf("name %s taken already", nh.CanonicalName())) return errors.New("ui.rename_noname_tip")
return lc.Get("ui.rename_taken"), fmt.Errorf(lc.Get("ui.rename_taken_tip", &l18n.Replacements{"name": "<a href='/hypha/%[1]s'>%[1]s</a>"}), nh.CanonicalName())
} }
if nh.CanonicalName() == "" { if !hyphae.IsValidName(newName) {
rejectRenameLog(oh, u, "no new name given") rejectRenameLog(oldHypha, u, fmt.Sprintf("new name %s invalid", newName))
return lc.Get("ui.rename_noname"), errors.New(lc.Get("ui.rename_noname_tip")) return errors.New("ui.rename_badname_tip") // FIXME: There is a bug related to this.
} }
if !hyphae.IsValidName(nh.CanonicalName()) { switch targetHypha := hyphae.ByName(newName); targetHypha.(type) {
rejectRenameLog(oh, u, fmt.Sprintf("new name %s invalid", nh.CanonicalName())) case hyphae.ExistingHypha:
return lc.Get("ui.rename_badname"), errors.New(lc.Get("ui.rename_badname_tip", &l18n.Replacements{"chars": "<code>^?!:#@&gt;&lt;*|\"\\'&amp;%</code>"})) rejectRenameLog(oldHypha, u, fmt.Sprintf("name %s taken already", newName))
} return errors.New("ui.rename_taken_tip") // FIXME: There is a bug related to this.
return "", nil
}
// 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 RenameHypha(h hyphae.Hypha, newHypha hyphae.Hypha, recursive bool, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) {
newHypha.Lock()
defer newHypha.Unlock()
hop = history.Operation(history.TypeRenameHypha)
if errtitle, err := CanRename(u, h, lc); errtitle != "" {
hop.WithErrAbort(err)
return hop, errtitle
}
if errtitle, err := canRenameThisToThat(h, newHypha, u, lc); errtitle != "" {
hop.WithErrAbort(err)
return hop, errtitle
} }
var ( var (
re = regexp.MustCompile(`(?i)` + h.CanonicalName()) re = regexp.MustCompile(`(?i)` + oldHypha.CanonicalName())
replaceName = func(str string) string { replaceName = func(str string) string {
return re.ReplaceAllString(util.CanonicalName(str), newHypha.CanonicalName()) return re.ReplaceAllString(util.CanonicalName(str), newName)
} }
hyphaeToRename = findHyphaeToRename(h.(hyphae.ExistingHypha), recursive) hyphaeToRename = findHyphaeToRename(oldHypha, recursive)
renameMap, err = renamingPairs(hyphaeToRename, replaceName) renameMap, err = renamingPairs(hyphaeToRename, replaceName)
renameMsg = "Rename %s to %s"
) )
if err != nil { if err != nil {
hop.Errs = append(hop.Errs, err) return err
return hop, hop.FirstErrorText()
} }
if recursive && len(hyphaeToRename) > 0 {
renameMsg += " recursively" hop := history.Operation(history.TypeRenameHypha).WithUser(u)
if len(hyphaeToRename) > 0 {
hop.WithMsg(fmt.Sprintf(
"Rename %s to %s recursively",
oldHypha.CanonicalName(),
newName))
} else {
hop.WithMsg(fmt.Sprintf(
"Rename %s to %s",
oldHypha.CanonicalName(),
newName))
} }
hop.WithFilesRenamed(renameMap).
WithMsg(fmt.Sprintf(renameMsg, h.CanonicalName(), newHypha.CanonicalName())). hop.WithFilesRenamed(renameMap).Apply()
WithUser(u).
Apply() if len(hop.Errs) != 0 {
if len(hop.Errs) == 0 { return hop.Errs[0]
for _, H := range hyphaeToRename {
h := H.(hyphae.ExistingHypha) // ontology think
oldName := h.CanonicalName()
hyphae.RenameHyphaTo(h, replaceName(h.CanonicalName()), replaceName)
backlinks.UpdateBacklinksAfterRename(h, oldName)
}
} }
return hop, ""
for _, h := range hyphaeToRename {
oldName := h.CanonicalName()
hyphae.RenameHyphaTo(h, replaceName(h.CanonicalName()), replaceName)
backlinks.UpdateBacklinksAfterRename(h, oldName)
}
return nil
} }
func findHyphaeToRename(superhypha hyphae.ExistingHypha, recursive bool) []hyphae.ExistingHypha { func findHyphaeToRename(superhypha hyphae.ExistingHypha, recursive bool) []hyphae.ExistingHypha {

View File

@ -10,16 +10,15 @@ import (
) )
// UnattachHypha unattaches hypha and makes a history record about that. // UnattachHypha unattaches hypha and makes a history record about that.
func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *history.Op, errtitle string) { func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
hop = history.Operation(history.TypeUnattachHypha)
if errtitle, err := CanUnattach(u, h, lc); errtitle != "" { if err := CanUnattach(u, h, lc); err != nil {
hop.WithErrAbort(err) return err
return hop, errtitle
} }
H := h.(*hyphae.MediaHypha) H := h.(*hyphae.MediaHypha)
hop. hop := history.
Operation(history.TypeUnattachHypha).
WithFilesRemoved(H.MediaFilePath()). WithFilesRemoved(H.MediaFilePath()).
WithMsg(fmt.Sprintf("Unattach %s", h.CanonicalName())). WithMsg(fmt.Sprintf("Unattach %s", h.CanonicalName())).
WithUser(u). WithUser(u).
@ -28,7 +27,7 @@ func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *histo
if len(hop.Errs) > 0 { if len(hop.Errs) > 0 {
rejectUnattachLog(h, u, "fail") rejectUnattachLog(h, u, "fail")
// FIXME: something may be wrong here // FIXME: something may be wrong here
return hop.WithErrAbort(fmt.Errorf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs)), "Error" return fmt.Errorf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs)
} }
if H.HasTextFile() { if H.HasTextFile() {
@ -36,5 +35,5 @@ func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *histo
} else { } else {
hyphae.DeleteHypha(H) hyphae.DeleteHypha(H)
} }
return hop, "" return nil
} }

View File

@ -47,8 +47,8 @@ func writeTextToDisk(h hyphae.ExistingHypha, data []byte, hop *history.Op) error
} }
// UploadText edits the hypha's text part and makes a history record about that. // UploadText edits the hypha's text part and makes a history record about that.
func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) { func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, lc *l18n.Localizer) error {
hop = history. hop := history.
Operation(history.TypeEditText). Operation(history.TypeEditText).
WithMsg(historyMessageForTextUpload(h, userMessage)). WithMsg(historyMessageForTextUpload(h, userMessage)).
WithUser(u) WithUser(u)
@ -56,28 +56,27 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, l
// Privilege check // Privilege check
if !u.CanProceed("upload-text") { if !u.CanProceed("upload-text") {
rejectEditLog(h, u, "no rights") rejectEditLog(h, u, "no rights")
return hop.WithErrAbort(errors.New(lc.Get("ui.act_norights_edit"))), lc.Get("ui.act_no_rights") hop.Abort()
return errors.New("ui.act_no_rights")
} }
// Hypha name exploit check // Hypha name exploit check
if !hyphae.IsValidName(h.CanonicalName()) { if !hyphae.IsValidName(h.CanonicalName()) {
// We check for the name only. I suppose the filepath would be valid as well. // We check for the name only. I suppose the filepath would be valid as well.
err := errors.New("invalid hypha name") hop.Abort()
return hop.WithErrAbort(err), err.Error() return errors.New("invalid hypha name")
} }
// Empty data check // Empty data check
if len(bytes.TrimSpace(data)) == 0 { // if nothing but whitespace if len(bytes.TrimSpace(data)) == 0 { // if nothing but whitespace
switch h.(type) { switch h.(type) {
case *hyphae.EmptyHypha: case *hyphae.EmptyHypha, *hyphae.MediaHypha:
// It's ok, just like cancel button.
return hop.Abort(), ""
case *hyphae.MediaHypha:
// Writing no description, it's ok, just like cancel button. // Writing no description, it's ok, just like cancel button.
return hop.Abort(), "" hop.Abort()
return nil
case *hyphae.TextualHypha: case *hyphae.TextualHypha:
// What do you want passing nothing for a textual hypha? // What do you want passing nothing for a textual hypha?
return hop.WithErrAbort(errors.New("No data passed")), "Empty" return errors.New("No data passed")
} }
} }
@ -89,49 +88,57 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, l
err := writeTextToDisk(H, data, hop) err := writeTextToDisk(H, data, hop)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() hop.Abort()
return err
} }
hyphae.Insert(H) hyphae.Insert(H)
case *hyphae.MediaHypha: case *hyphae.MediaHypha:
oldText, err := FetchTextFile(h) oldText, err := FetchTextFile(h)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() hop.Abort()
return err
} }
// TODO: that []byte(...) part should be removed // TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 { if bytes.Compare(data, []byte(oldText)) == 0 {
// No changes! Just like cancel button // No changes! Just like cancel button
return hop.Abort(), "" hop.Abort()
return nil
} }
err = writeTextToDisk(h, data, hop) err = writeTextToDisk(h, data, hop)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() hop.Abort()
return err
} }
backlinks.UpdateBacklinksAfterEdit(h, oldText) backlinks.UpdateBacklinksAfterEdit(h, oldText)
case *hyphae.TextualHypha: case *hyphae.TextualHypha:
oldText, err := FetchTextFile(h) oldText, err := FetchTextFile(h)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() hop.Abort()
return err
} }
// TODO: that []byte(...) part should be removed // TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 { if bytes.Compare(data, []byte(oldText)) == 0 {
// No changes! Just like cancel button // No changes! Just like cancel button
return hop.Abort(), "" hop.Abort()
return nil
} }
err = writeTextToDisk(h, data, hop) err = writeTextToDisk(h, data, hop)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() hop.Abort()
return err
} }
backlinks.UpdateBacklinksAfterEdit(h, oldText) backlinks.UpdateBacklinksAfterEdit(h, oldText)
} }
return hop.Apply(), "" hop.Apply()
return nil
} }
func historyMessageForMediaUpload(h hyphae.Hypha, mime string) string { func historyMessageForMediaUpload(h hyphae.Hypha, mime string) string {
@ -157,40 +164,35 @@ func writeMediaToDisk(h hyphae.Hypha, mime string, data []byte) (string, error)
} }
// UploadBinary edits the hypha's media part 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.Hypha, mime string, file multipart.File, u *user.User, lc *l18n.Localizer) (*history.Op, string) { func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User, lc *l18n.Localizer) error {
hop := history.
Operation(history.TypeEditBinary).
WithMsg(historyMessageForMediaUpload(h, mime)).
WithUser(u)
// Privilege check // Privilege check
if !u.CanProceed("upload-binary") { if !u.CanProceed("upload-binary") {
rejectAttachLog(h, u, "no rights") rejectAttachLog(h, u, "no rights")
return hop.WithErrAbort(errors.New(lc.Get("ui.act_norights_attach"))), lc.Get("ui.act_no_rights") return errors.New("ui.act_no_rights")
} }
// Hypha name exploit check // Hypha name exploit check
if !hyphae.IsValidName(h.CanonicalName()) { if !hyphae.IsValidName(h.CanonicalName()) {
// We check for the name only. I suppose the filepath would be valid as well. // We check for the name only. I suppose the filepath would be valid as well.
err := errors.New("invalid hypha name") return errors.New("invalid hypha name")
return hop.WithErrAbort(err), err.Error()
} }
data, err := io.ReadAll(file) data, err := io.ReadAll(file)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() return err
} }
// Empty data check // Empty data check
if len(data) == 0 { if len(data) == 0 {
return hop.WithErrAbort(errors.New("No data passed")), "Empty" return errors.New("No data passed")
} }
// At this point, we have a savable media document. Gotta save it. // At this point, we have a savable media document. Gotta save it.
uploadedFilePath, err := writeMediaToDisk(h, mime, data) uploadedFilePath, err := writeMediaToDisk(h, mime, data)
if err != nil { if err != nil {
return hop.WithErrAbort(err), err.Error() return err
} }
switch h := h.(type) { switch h := h.(type) {
@ -203,12 +205,18 @@ func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User
prevFilePath := h.MediaFilePath() prevFilePath := h.MediaFilePath()
if prevFilePath != uploadedFilePath { if prevFilePath != uploadedFilePath {
if err := history.Rename(prevFilePath, uploadedFilePath); err != nil { if err := history.Rename(prevFilePath, uploadedFilePath); err != nil {
return hop.WithErrAbort(err), err.Error() return err
} }
log.Printf("Move %s to %s\n", prevFilePath, uploadedFilePath) log.Printf("Move %s to %s\n", prevFilePath, uploadedFilePath)
h.SetMediaFilePath(uploadedFilePath) h.SetMediaFilePath(uploadedFilePath)
} }
} }
return hop.WithFiles(uploadedFilePath).Apply(), "" history.
Operation(history.TypeEditBinary).
WithMsg(historyMessageForMediaUpload(h, mime)).
WithUser(u).
WithFiles(uploadedFilePath).
Apply()
return nil
} }

View File

@ -34,11 +34,14 @@ var minimalRights = map[string]int{
"edit": 1, "edit": 1,
"upload-binary": 1, "upload-binary": 1,
"upload-text": 1, "upload-text": 1,
"rename": 2,
"rename-ask": 2, "rename-ask": 2,
"rename-confirm": 2, "rename-confirm": 2,
"remove-media": 2,
"unattach-ask": 2, "unattach-ask": 2,
"unattach-confirm": 2, "unattach-confirm": 2,
"update-header-links": 3, "update-header-links": 3,
"delete": 3,
"delete-ask": 3, "delete-ask": 3,
"delete-confirm": 3, "delete-confirm": 3,
"reindex": 4, "reindex": 4,

View File

@ -2,7 +2,7 @@
{% import "net/http" %} {% import "net/http" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %} {% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) %} {% func DeleteAskHTML(rq *http.Request, hyphaName string) %}
{% code {% code
lc := l18n.FromRequest(rq) lc := l18n.FromRequest(rq)
%} %}
@ -16,7 +16,7 @@
{%= modalEnd(hyphaName, true, lc) %} {%= modalEnd(hyphaName, true, lc) %}
{% endfunc %} {% endfunc %}
{% func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) %} {% func UnattachAskHTML(rq *http.Request, hyphaName string) %}
{% code {% code
lc := l18n.FromRequest(rq) lc := l18n.FromRequest(rq)
%} %}
@ -29,7 +29,7 @@
{%= modalEnd(hyphaName, true, lc) %} {%= modalEnd(hyphaName, true, lc) %}
{% endfunc %} {% endfunc %}
{% func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) %} {% func RenameAskHTML(rq *http.Request, hyphaName string) %}
{% code {% code
lc := l18n.FromRequest(rq) lc := l18n.FromRequest(rq)
%} %}

View File

@ -27,7 +27,7 @@ var (
) )
//line views/modal.qtpl:5 //line views/modal.qtpl:5
func StreamDeleteAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func StreamDeleteAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:5 //line views/modal.qtpl:5
qw422016.N().S(` qw422016.N().S(`
`) `)
@ -65,22 +65,22 @@ func StreamDeleteAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName
} }
//line views/modal.qtpl:17 //line views/modal.qtpl:17
func WriteDeleteAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func WriteDeleteAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:17 //line views/modal.qtpl:17
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/modal.qtpl:17 //line views/modal.qtpl:17
StreamDeleteAskHTML(qw422016, rq, hyphaName, isOld) StreamDeleteAskHTML(qw422016, rq, hyphaName)
//line views/modal.qtpl:17 //line views/modal.qtpl:17
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/modal.qtpl:17 //line views/modal.qtpl:17
} }
//line views/modal.qtpl:17 //line views/modal.qtpl:17
func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) string { func DeleteAskHTML(rq *http.Request, hyphaName string) string {
//line views/modal.qtpl:17 //line views/modal.qtpl:17
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/modal.qtpl:17 //line views/modal.qtpl:17
WriteDeleteAskHTML(qb422016, rq, hyphaName, isOld) WriteDeleteAskHTML(qb422016, rq, hyphaName)
//line views/modal.qtpl:17 //line views/modal.qtpl:17
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/modal.qtpl:17 //line views/modal.qtpl:17
@ -91,7 +91,7 @@ func DeleteAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
} }
//line views/modal.qtpl:19 //line views/modal.qtpl:19
func StreamUnattachAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func StreamUnattachAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:19 //line views/modal.qtpl:19
qw422016.N().S(` qw422016.N().S(`
`) `)
@ -124,22 +124,22 @@ func StreamUnattachAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaNam
} }
//line views/modal.qtpl:30 //line views/modal.qtpl:30
func WriteUnattachAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func WriteUnattachAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:30 //line views/modal.qtpl:30
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/modal.qtpl:30 //line views/modal.qtpl:30
StreamUnattachAskHTML(qw422016, rq, hyphaName, isOld) StreamUnattachAskHTML(qw422016, rq, hyphaName)
//line views/modal.qtpl:30 //line views/modal.qtpl:30
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/modal.qtpl:30 //line views/modal.qtpl:30
} }
//line views/modal.qtpl:30 //line views/modal.qtpl:30
func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) string { func UnattachAskHTML(rq *http.Request, hyphaName string) string {
//line views/modal.qtpl:30 //line views/modal.qtpl:30
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/modal.qtpl:30 //line views/modal.qtpl:30
WriteUnattachAskHTML(qb422016, rq, hyphaName, isOld) WriteUnattachAskHTML(qb422016, rq, hyphaName)
//line views/modal.qtpl:30 //line views/modal.qtpl:30
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/modal.qtpl:30 //line views/modal.qtpl:30
@ -150,7 +150,7 @@ func UnattachAskHTML(rq *http.Request, hyphaName string, isOld bool) string {
} }
//line views/modal.qtpl:32 //line views/modal.qtpl:32
func StreamRenameAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func StreamRenameAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:32 //line views/modal.qtpl:32
qw422016.N().S(` qw422016.N().S(`
`) `)
@ -201,22 +201,22 @@ func StreamRenameAskHTML(qw422016 *qt422016.Writer, rq *http.Request, hyphaName
} }
//line views/modal.qtpl:49 //line views/modal.qtpl:49
func WriteRenameAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string, isOld bool) { func WriteRenameAskHTML(qq422016 qtio422016.Writer, rq *http.Request, hyphaName string) {
//line views/modal.qtpl:49 //line views/modal.qtpl:49
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/modal.qtpl:49 //line views/modal.qtpl:49
StreamRenameAskHTML(qw422016, rq, hyphaName, isOld) StreamRenameAskHTML(qw422016, rq, hyphaName)
//line views/modal.qtpl:49 //line views/modal.qtpl:49
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/modal.qtpl:49 //line views/modal.qtpl:49
} }
//line views/modal.qtpl:49 //line views/modal.qtpl:49
func RenameAskHTML(rq *http.Request, hyphaName string, isOld bool) string { func RenameAskHTML(rq *http.Request, hyphaName string) string {
//line views/modal.qtpl:49 //line views/modal.qtpl:49
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/modal.qtpl:49 //line views/modal.qtpl:49
WriteRenameAskHTML(qb422016, rq, hyphaName, isOld) WriteRenameAskHTML(qb422016, rq, hyphaName)
//line views/modal.qtpl:49 //line views/modal.qtpl:49
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/modal.qtpl:49 //line views/modal.qtpl:49

View File

@ -23,7 +23,7 @@
<nav class="hypha-info"> <nav class="hypha-info">
<ul class="hypha-info__list"> <ul class="hypha-info__list">
{%= hyphaInfoEntry(h, u, "history", lc.Get("ui.history_link")) %} {%= hyphaInfoEntry(h, u, "history", lc.Get("ui.history_link")) %}
{%= hyphaInfoEntry(h, u, "rename-ask", lc.Get("ui.rename_link")) %} {%= hyphaInfoEntry(h, u, "rename", lc.Get("ui.rename_link")) %}
{%= hyphaInfoEntry(h, u, "delete-ask", lc.Get("ui.delete_link")) %} {%= hyphaInfoEntry(h, u, "delete-ask", lc.Get("ui.delete_link")) %}
{%= hyphaInfoEntry(h, u, "text", lc.Get("ui.text_link")) %} {%= hyphaInfoEntry(h, u, "text", lc.Get("ui.text_link")) %}
{%= hyphaInfoEntry(h, u, "attachment", lc.Get("ui.attachment_link")) %} {%= hyphaInfoEntry(h, u, "attachment", lc.Get("ui.attachment_link")) %}

View File

@ -122,7 +122,7 @@ func streamhyphaInfo(qw422016 *qt422016.Writer, rq *http.Request, h hyphae.Hypha
qw422016.N().S(` qw422016.N().S(`
`) `)
//line views/nav.qtpl:26 //line views/nav.qtpl:26
streamhyphaInfoEntry(qw422016, h, u, "rename-ask", lc.Get("ui.rename_link")) streamhyphaInfoEntry(qw422016, h, u, "rename", lc.Get("ui.rename_link"))
//line views/nav.qtpl:26 //line views/nav.qtpl:26
qw422016.N().S(` qw422016.N().S(`
`) `)

View File

@ -10,7 +10,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae" "github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n" "github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom" "github.com/bouncepaw/mycorrhiza/shroom"
@ -22,14 +21,13 @@ import (
func initMutators(r *mux.Router) { func initMutators(r *mux.Router) {
// Those that do not actually mutate anything: // Those that do not actually mutate anything:
r.PathPrefix("/edit/").HandlerFunc(handlerEdit) r.PathPrefix("/edit/").HandlerFunc(handlerEdit)
r.PathPrefix("/rename/").HandlerFunc(handlerRename).Methods("GET", "POST")
r.PathPrefix("/delete-ask/").HandlerFunc(handlerDeleteAsk) r.PathPrefix("/delete-ask/").HandlerFunc(handlerDeleteAsk)
r.PathPrefix("/rename-ask/").HandlerFunc(handlerRenameAsk)
r.PathPrefix("/unattach-ask/").HandlerFunc(handlerUnattachAsk) r.PathPrefix("/unattach-ask/").HandlerFunc(handlerUnattachAsk)
// And those that do mutate something: // And those that do mutate something:
r.PathPrefix("/upload-binary/").HandlerFunc(handlerUploadBinary) r.PathPrefix("/upload-binary/").HandlerFunc(handlerUploadBinary)
r.PathPrefix("/upload-text/").HandlerFunc(handlerUploadText) r.PathPrefix("/upload-text/").HandlerFunc(handlerUploadText)
r.PathPrefix("/delete-confirm/").HandlerFunc(handlerDeleteConfirm) r.PathPrefix("/delete-confirm/").HandlerFunc(handlerDeleteConfirm)
r.PathPrefix("/rename-confirm/").HandlerFunc(handlerRenameConfirm)
r.PathPrefix("/unattach-confirm/").HandlerFunc(handlerUnattachConfirm) r.PathPrefix("/unattach-confirm/").HandlerFunc(handlerUnattachConfirm)
} }
@ -37,9 +35,9 @@ func initMutators(r *mux.Router) {
func factoryHandlerAsker( func factoryHandlerAsker(
actionPath string, actionPath string,
asker func(*user.User, hyphae.Hypha, *l18n.Localizer) (string, error), asker func(*user.User, hyphae.Hypha, *l18n.Localizer) error,
succTitleKey string, succTitleKey string,
succPageTemplate func(*http.Request, string, bool) string, succPageTemplate func(*http.Request, string) string,
) func(http.ResponseWriter, *http.Request) { ) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, rq *http.Request) { return func(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
@ -49,13 +47,12 @@ func factoryHandlerAsker(
u = user.FromRequest(rq) u = user.FromRequest(rq)
lc = l18n.FromRequest(rq) lc = l18n.FromRequest(rq)
) )
if errtitle, err := asker(u, h, lc); err != nil { if err := asker(u, h, lc); err != nil {
httpErr( httpErr(
w, w,
lc, lc,
http.StatusInternalServerError, http.StatusInternalServerError,
hyphaName, hyphaName,
errtitle,
err.Error()) err.Error())
return return
} }
@ -63,14 +60,7 @@ func factoryHandlerAsker(
w, w,
views.BaseHTML( views.BaseHTML(
fmt.Sprintf(lc.Get(succTitleKey), util.BeautifulName(hyphaName)), fmt.Sprintf(lc.Get(succTitleKey), util.BeautifulName(hyphaName)),
succPageTemplate(rq, hyphaName, func(h hyphae.Hypha) bool { succPageTemplate(rq, hyphaName),
switch h.(type) {
case *hyphae.EmptyHypha:
return false
default:
return true
}
}(h)),
lc, lc,
u)) u))
} }
@ -90,16 +80,9 @@ var handlerDeleteAsk = factoryHandlerAsker(
views.DeleteAskHTML, views.DeleteAskHTML,
) )
var handlerRenameAsk = factoryHandlerAsker(
"rename-ask",
shroom.CanRename,
"ui.ask_rename",
views.RenameAskHTML,
)
func factoryHandlerConfirmer( func factoryHandlerConfirmer(
actionPath string, actionPath string,
confirmer func(hyphae.Hypha, *user.User, *http.Request) (*history.Op, string), confirmer func(hyphae.Hypha, *user.User, *http.Request) error,
) func(http.ResponseWriter, *http.Request) { ) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, rq *http.Request) { return func(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
@ -109,10 +92,9 @@ func factoryHandlerConfirmer(
u = user.FromRequest(rq) u = user.FromRequest(rq)
lc = l18n.FromRequest(rq) lc = l18n.FromRequest(rq)
) )
if hop, errtitle := confirmer(h, u, rq); hop.HasErrors() { if err := confirmer(h, u, rq); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, httpErr(w, lc, http.StatusInternalServerError, hyphaName,
errtitle, err.Error())
hop.FirstErrorText())
return return
} }
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther) http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
@ -121,35 +103,52 @@ func factoryHandlerConfirmer(
var handlerUnattachConfirm = factoryHandlerConfirmer( var handlerUnattachConfirm = factoryHandlerConfirmer(
"unattach-confirm", "unattach-confirm",
func(h hyphae.Hypha, u *user.User, rq *http.Request) (*history.Op, string) { func(h hyphae.Hypha, u *user.User, rq *http.Request) error {
return shroom.UnattachHypha(u, h, l18n.FromRequest(rq)) return shroom.UnattachHypha(u, h, l18n.FromRequest(rq))
}, },
) )
var handlerDeleteConfirm = factoryHandlerConfirmer( var handlerDeleteConfirm = factoryHandlerConfirmer(
"delete-confirm", "delete-confirm",
func(h hyphae.Hypha, u *user.User, rq *http.Request) (*history.Op, string) { func(h hyphae.Hypha, u *user.User, rq *http.Request) error {
return shroom.DeleteHypha(u, h, l18n.FromRequest(rq)) return shroom.DeleteHypha(u, h, l18n.FromRequest(rq))
}, },
) )
// handlerRenameConfirm should redirect to the new hypha, thus it's out of factory func handlerRename(w http.ResponseWriter, rq *http.Request) {
func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
var ( var (
u = user.FromRequest(rq) u = user.FromRequest(rq)
lc = l18n.FromRequest(rq) lc = l18n.FromRequest(rq)
hyphaName = util.HyphaNameFromRq(rq, "rename-confirm") h = hyphae.ByName(util.HyphaNameFromRq(rq, "rename-confirm"))
oldHypha = hyphae.ByName(hyphaName) )
switch h.(type) {
case *hyphae.EmptyHypha:
log.Printf("%s tries to rename empty hypha %s", u.Name, h.CanonicalName())
httpErr(w, lc, http.StatusForbidden, h.CanonicalName(), "Cannot rename an empty hypha") // TODO: localize
return
}
var (
oldHypha = h.(hyphae.ExistingHypha)
newName = util.CanonicalName(rq.PostFormValue("new-name")) newName = util.CanonicalName(rq.PostFormValue("new-name"))
newHypha = hyphae.ByName(newName)
recursive = rq.PostFormValue("recursive") == "true" recursive = rq.PostFormValue("recursive") == "true"
) )
hop, errtitle := shroom.RenameHypha(oldHypha, newHypha, recursive, u, lc)
if hop.HasErrors() { if rq.Method == "GET" {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, util.HTTP200Page(
errtitle, w,
hop.FirstErrorText()) views.BaseHTML(
fmt.Sprintf(lc.Get("ui.ask_rename"), util.BeautifulName(oldHypha.CanonicalName())),
views.RenameAskHTML(rq, oldHypha.CanonicalName()),
lc,
u))
}
if err := shroom.Rename(oldHypha, newName, recursive, u); err != nil {
log.Printf("%s tries to rename %s: %s", u.Name, oldHypha.CanonicalName(), err.Error())
httpErr(w, lc, http.StatusForbidden, oldHypha.CanonicalName(), lc.Get(err.Error())) // TODO: localize
return return
} }
http.Redirect(w, rq, "/hypha/"+newName, http.StatusSeeOther) http.Redirect(w, rq, "/hypha/"+newName, http.StatusSeeOther)
@ -167,9 +166,8 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
u = user.FromRequest(rq) u = user.FromRequest(rq)
lc = l18n.FromRequest(rq) lc = l18n.FromRequest(rq)
) )
if errtitle, err := shroom.CanEdit(u, h, lc); err != nil { if err := shroom.CanEdit(u, h, lc); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, httpErr(w, lc, http.StatusInternalServerError, hyphaName,
errtitle,
err.Error()) err.Error())
return return
} }
@ -181,7 +179,6 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
if err != nil { if err != nil {
log.Println(err) log.Println(err)
httpErr(w, lc, http.StatusInternalServerError, hyphaName, httpErr(w, lc, http.StatusInternalServerError, hyphaName,
lc.Get("ui.error"),
lc.Get("ui.error_text_fetch")) lc.Get("ui.error_text_fetch"))
return return
} }
@ -206,16 +203,11 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
message = rq.PostFormValue("message") message = rq.PostFormValue("message")
u = user.FromRequest(rq) u = user.FromRequest(rq)
lc = l18n.FromRequest(rq) lc = l18n.FromRequest(rq)
hop *history.Op
errtitle string
) )
if action != "Preview" { if action != "Preview" {
hop, errtitle = shroom.UploadText(h, []byte(textData), message, u, lc) if err := shroom.UploadText(h, []byte(textData), message, u, lc); err != nil {
if hop.HasErrors() { httpErr(w, lc, http.StatusForbidden, hyphaName, err.Error())
httpErr(w, lc, http.StatusForbidden, hyphaName,
errtitle,
hop.FirstErrorText())
return return
} }
} }
@ -254,12 +246,10 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
) )
if err != nil { if err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, httpErr(w, lc, http.StatusInternalServerError, hyphaName,
lc.Get("ui.error"),
err.Error()) err.Error())
} }
if errtitle, err := shroom.CanAttach(u, h, lc); err != nil { if err := shroom.CanAttach(u, h, lc); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, httpErr(w, lc, http.StatusInternalServerError, hyphaName,
errtitle,
err.Error()) err.Error())
} }
@ -272,13 +262,13 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
if file != nil { if file != nil {
defer file.Close() defer file.Close()
} }
var ( var (
mime = handler.Header.Get("Content-Type") mime = handler.Header.Get("Content-Type")
hop, errtitle = shroom.UploadBinary(h, mime, file, u, lc)
) )
if hop.HasErrors() { if err := shroom.UploadBinary(h, mime, file, u, lc); err != nil {
httpErr(w, lc, http.StatusInternalServerError, hyphaName, errtitle, hop.FirstErrorText()) httpErr(w, lc, http.StatusInternalServerError, hyphaName, err.Error())
return return
} }
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther) http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)

View File

@ -104,7 +104,7 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
if ok := user.CanProceed(rq, "reindex"); !ok { if ok := user.CanProceed(rq, "reindex"); !ok {
var lc = l18n.FromRequest(rq) var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.no_rights"), lc.Get("ui.reindex_no_rights")) httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.reindex_no_rights"))
log.Println("Rejected", rq.URL) log.Println("Rejected", rq.URL)
return return
} }
@ -122,7 +122,7 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
if ok := user.CanProceed(rq, "update-header-links"); !ok { if ok := user.CanProceed(rq, "update-header-links"); !ok {
var lc = l18n.FromRequest(rq) var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.no_rights"), lc.Get("ui.header_no_rights")) httpErr(w, lc, http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.header_no_rights"))
log.Println("Rejected", rq.URL) log.Println("Rejected", rq.URL)
return return
} }
@ -139,7 +139,7 @@ func handlerRandom(w http.ResponseWriter, rq *http.Request) {
) )
if amountOfHyphae == 0 { if amountOfHyphae == 0 {
var lc = l18n.FromRequest(rq) var lc = l18n.FromRequest(rq)
httpErr(w, lc, http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae"), lc.Get("ui.random_no_hyphae_tip")) httpErr(w, lc, http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae_tip"))
return return
} }
i := rand.Intn(amountOfHyphae) i := rand.Intn(amountOfHyphae)

View File

@ -6,7 +6,6 @@ package web
import ( import (
"fmt" "fmt"
"io" "io"
"log"
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
@ -24,14 +23,13 @@ import (
var stylesheets = []string{"default.css", "custom.css"} var stylesheets = []string{"default.css", "custom.css"}
// httpErr is used by many handlers to signal errors in a compact way. // httpErr is used by many handlers to signal errors in a compact way.
func httpErr(w http.ResponseWriter, lc *l18n.Localizer, status int, name, title, errMsg string) { func httpErr(w http.ResponseWriter, lc *l18n.Localizer, status int, name, errMsg string) {
log.Println(errMsg, "for", name)
w.Header().Set("Content-Type", mime.TypeByExtension(".html")) w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
w.WriteHeader(status) w.WriteHeader(status)
fmt.Fprint( fmt.Fprint(
w, w,
views.BaseHTML( views.BaseHTML(
title, "Error",
fmt.Sprintf( fmt.Sprintf(
`<main class="main-width"><p>%s. <a href="/hypha/%s">%s<a></p></main>`, `<main class="main-width"><p>%s. <a href="/hypha/%s">%s<a></p></main>`,
errMsg, errMsg,