1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-05 02:29:54 +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"
// 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 {
sync.RWMutex
@ -12,6 +13,7 @@ func (e *EmptyHypha) CanonicalName() string {
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 {
return &TextualHypha{
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 {
return &MediaHypha{
canonicalName: e.CanonicalName(),

View File

@ -4,20 +4,17 @@ import (
"github.com/bouncepaw/mycorrhiza/util"
)
// ExistingHypha is not EmptyHypha.
// ExistingHypha is not EmptyHypha. *MediaHypha and *TextualHypha implement this interface.
type ExistingHypha interface {
Hypha
// DoesExist does nothing except marks that the type is an ExistingHypha.
DoesExist()
HasTextFile() bool
TextFilePath() string
}
// 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) {
// TODO: that replaceName is suspicious.
// TODO: that replaceName is suspicious, get rid of it.
newName = util.CanonicalName(newName)
byNamesMutex.Lock()
h.Lock()
@ -37,3 +34,28 @@ func RenameHyphaTo(h ExistingHypha, newName string, replaceName func(string) str
byNamesMutex.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"
)
// HyphaPattern is a pattern which all hyphae names must match.
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
// hyphaNamePattern is a pattern which all hyphae names must match.
var hyphaNamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
// IsValidName checks for invalid characters and path traversals.
func IsValidName(hyphaName string) bool {
if !HyphaPattern.MatchString(hyphaName) {
if !hyphaNamePattern.MatchString(hyphaName) {
return false
}
for _, segment := range strings.Split(hyphaName, "/") {
@ -22,38 +22,16 @@ func IsValidName(hyphaName string) bool {
return true
}
// Hypha is a hypha you know and love.
// Hypha is the hypha you know and love.
type Hypha interface {
sync.Locker
// CanonicalName returns the canonical name of the hypha.
//
// util.CanonicalName(h.CanonicalName()) == h.CanonicalName()
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.
func ByName(hyphaName string) (h Hypha) {
byNamesMutex.Lock()

View File

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

View File

@ -4,6 +4,7 @@ import (
"sync"
)
// TextualHypha is a hypha with text, and nothing else. An article, a note, a poem, whatnot.
type TextualHypha struct {
sync.RWMutex
@ -11,9 +12,6 @@ type TextualHypha struct {
mycoFilePath string
}
func (t *TextualHypha) DoesExist() {
}
func (t *TextualHypha) CanonicalName() string {
return t.canonicalName
}
@ -26,6 +24,7 @@ func (t *TextualHypha) TextFilePath() string {
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 {
return &MediaHypha{
canonicalName: t.CanonicalName(),

View File

@ -17,29 +17,29 @@ func canFactory(
noRightsMsg string,
notExistsMsg string,
mustExist bool,
) func(*user.User, hyphae.Hypha, *l18n.Localizer) (string, error) {
return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (string, error) {
) func(*user.User, hyphae.Hypha, *l18n.Localizer) error {
return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
if !u.CanProceed(action) {
rejectLogger(h, u, "no rights")
return lc.Get("ui.act_no_rights"), errors.New(lc.Get(noRightsMsg))
return errors.New(noRightsMsg)
}
if mustExist {
switch h.(type) {
case *hyphae.EmptyHypha:
rejectLogger(h, u, "does not exist")
return lc.Get("ui.act_notexist"), errors.New(lc.Get(notExistsMsg))
return errors.New(notExistsMsg)
}
}
if dispatcher == nil {
return "", nil
return nil
}
errmsg, errtitle := dispatcher(h, u, lc)
if errtitle == "" {
return "", nil
return nil
}
return errtitle, errors.New(errmsg)
return errors.New(errmsg)
}
}
@ -54,22 +54,12 @@ var (
true,
)
CanRename = canFactory(
rejectRenameLog,
"rename-confirm",
nil,
"ui.act_norights_rename",
"ui.act_notexist_rename",
true,
)
CanUnattach = canFactory(
rejectUnattachLog,
"unattach-confirm",
func(h hyphae.Hypha, u *user.User, lc *l18n.Localizer) (errmsg, errtitle string) {
switch h := h.(type) {
case *hyphae.EmptyHypha:
case *hyphae.TextualHypha:
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
rejectUnattachLog(h, u, "no amnt")
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.
func DeleteHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *history.Op, errtitle string) {
hop = history.
func DeleteHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
if err := CanDelete(u, h, lc); err != nil {
return err
}
hop := history.
Operation(history.TypeDeleteHypha).
WithMsg(fmt.Sprintf("Delete %s", h.CanonicalName())).
WithUser(u)
if errtitle, err := CanDelete(u, h, lc); errtitle != "" {
hop.WithErrAbort(err)
return hop, errtitle
}
switch h := h.(type) {
case *hyphae.MediaHypha:
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)
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/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
func canRenameThisToThat(oh hyphae.Hypha, nh hyphae.Hypha, u *user.User, lc *l18n.Localizer) (errtitle string, err error) {
switch nh.(type) {
case *hyphae.EmptyHypha:
default:
rejectRenameLog(oh, u, fmt.Sprintf("name %s taken already", nh.CanonicalName()))
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())
// Rename renames the old hypha to the new name. Call if and only if the user has the permission to rename.
func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, u *user.User) error {
if newName == "" {
rejectRenameLog(oldHypha, u, "no new name given")
return errors.New("ui.rename_noname_tip")
}
if nh.CanonicalName() == "" {
rejectRenameLog(oh, u, "no new name given")
return lc.Get("ui.rename_noname"), errors.New(lc.Get("ui.rename_noname_tip"))
if !hyphae.IsValidName(newName) {
rejectRenameLog(oldHypha, u, fmt.Sprintf("new name %s invalid", newName))
return errors.New("ui.rename_badname_tip") // FIXME: There is a bug related to this.
}
if !hyphae.IsValidName(nh.CanonicalName()) {
rejectRenameLog(oh, u, fmt.Sprintf("new name %s invalid", nh.CanonicalName()))
return lc.Get("ui.rename_badname"), errors.New(lc.Get("ui.rename_badname_tip", &l18n.Replacements{"chars": "<code>^?!:#@&gt;&lt;*|\"\\'&amp;%</code>"}))
}
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
switch targetHypha := hyphae.ByName(newName); targetHypha.(type) {
case hyphae.ExistingHypha:
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.
}
var (
re = regexp.MustCompile(`(?i)` + h.CanonicalName())
re = regexp.MustCompile(`(?i)` + oldHypha.CanonicalName())
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)
renameMsg = "Rename %s to %s"
)
if err != nil {
hop.Errs = append(hop.Errs, err)
return hop, hop.FirstErrorText()
return err
}
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())).
WithUser(u).
Apply()
if len(hop.Errs) == 0 {
for _, H := range hyphaeToRename {
h := H.(hyphae.ExistingHypha) // ontology think
hop.WithFilesRenamed(renameMap).Apply()
if len(hop.Errs) != 0 {
return hop.Errs[0]
}
for _, h := range hyphaeToRename {
oldName := h.CanonicalName()
hyphae.RenameHyphaTo(h, replaceName(h.CanonicalName()), replaceName)
backlinks.UpdateBacklinksAfterRename(h, oldName)
}
}
return hop, ""
return nil
}
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.
func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *history.Op, errtitle string) {
hop = history.Operation(history.TypeUnattachHypha)
func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
if errtitle, err := CanUnattach(u, h, lc); errtitle != "" {
hop.WithErrAbort(err)
return hop, errtitle
if err := CanUnattach(u, h, lc); err != nil {
return err
}
H := h.(*hyphae.MediaHypha)
hop.
hop := history.
Operation(history.TypeUnattachHypha).
WithFilesRemoved(H.MediaFilePath()).
WithMsg(fmt.Sprintf("Unattach %s", h.CanonicalName())).
WithUser(u).
@ -28,7 +27,7 @@ func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *histo
if len(hop.Errs) > 0 {
rejectUnattachLog(h, u, "fail")
// 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() {
@ -36,5 +35,5 @@ func UnattachHypha(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) (hop *histo
} else {
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.
func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) {
hop = history.
func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, lc *l18n.Localizer) error {
hop := history.
Operation(history.TypeEditText).
WithMsg(historyMessageForTextUpload(h, userMessage)).
WithUser(u)
@ -56,28 +56,27 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User, l
// 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")
hop.Abort()
return errors.New("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()
hop.Abort()
return errors.New("invalid hypha name")
}
// Empty data check
if len(bytes.TrimSpace(data)) == 0 { // if nothing but whitespace
switch h.(type) {
case *hyphae.EmptyHypha:
// It's ok, just like cancel button.
return hop.Abort(), ""
case *hyphae.MediaHypha:
case *hyphae.EmptyHypha, *hyphae.MediaHypha:
// Writing no description, it's ok, just like cancel button.
return hop.Abort(), ""
hop.Abort()
return nil
case *hyphae.TextualHypha:
// 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)
if err != nil {
return hop.WithErrAbort(err), err.Error()
hop.Abort()
return err
}
hyphae.Insert(H)
case *hyphae.MediaHypha:
oldText, err := FetchTextFile(h)
if err != nil {
return hop.WithErrAbort(err), err.Error()
hop.Abort()
return err
}
// TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 {
// No changes! Just like cancel button
return hop.Abort(), ""
hop.Abort()
return nil
}
err = writeTextToDisk(h, data, hop)
if err != nil {
return hop.WithErrAbort(err), err.Error()
hop.Abort()
return err
}
backlinks.UpdateBacklinksAfterEdit(h, oldText)
case *hyphae.TextualHypha:
oldText, err := FetchTextFile(h)
if err != nil {
return hop.WithErrAbort(err), err.Error()
hop.Abort()
return err
}
// TODO: that []byte(...) part should be removed
if bytes.Compare(data, []byte(oldText)) == 0 {
// No changes! Just like cancel button
return hop.Abort(), ""
hop.Abort()
return nil
}
err = writeTextToDisk(h, data, hop)
if err != nil {
return hop.WithErrAbort(err), err.Error()
hop.Abort()
return err
}
backlinks.UpdateBacklinksAfterEdit(h, oldText)
}
return hop.Apply(), ""
hop.Apply()
return nil
}
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.
func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User, lc *l18n.Localizer) (*history.Op, string) {
hop := history.
Operation(history.TypeEditBinary).
WithMsg(historyMessageForMediaUpload(h, mime)).
WithUser(u)
func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User, lc *l18n.Localizer) error {
// Privilege check
if !u.CanProceed("upload-binary") {
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
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()
return errors.New("invalid hypha name")
}
data, err := io.ReadAll(file)
if err != nil {
return hop.WithErrAbort(err), err.Error()
return err
}
// Empty data check
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.
uploadedFilePath, err := writeMediaToDisk(h, mime, data)
if err != nil {
return hop.WithErrAbort(err), err.Error()
return err
}
switch h := h.(type) {
@ -203,12 +205,18 @@ func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User
prevFilePath := h.MediaFilePath()
if prevFilePath != uploadedFilePath {
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)
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,
"upload-binary": 1,
"upload-text": 1,
"rename": 2,
"rename-ask": 2,
"rename-confirm": 2,
"remove-media": 2,
"unattach-ask": 2,
"unattach-confirm": 2,
"update-header-links": 3,
"delete": 3,
"delete-ask": 3,
"delete-confirm": 3,
"reindex": 4,

View File

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

View File

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

View File

@ -23,7 +23,7 @@
<nav class="hypha-info">
<ul class="hypha-info__list">
{%= 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, "text", lc.Get("ui.text_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(`
`)
//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
qw422016.N().S(`
`)

View File

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

View File

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