mycorrhiza/internal/shroom/upload.go
Timur Ismagilov 41733c50bd
New templates #117 (#236)
Didn't have the chance to migrate //all// templates just yet. We'll get there.

* Implement yet another template system

* Move orphans to the new system and fix a bug in it

* Link orphans in the admin panel

* Move the backlink handlers to the web package

* Move auth routing to web

* Move /user-list to the new system

* Move change password and translate it

* Move stuff

* Move admin-related stuff to the web

* Move a lot of files into internal dir

Outside of it are web and stuff that needs further refactoring

* Fix static not loading and de-qtpl tree

* Move tree to internal

* Keep the globe on the same line #230

* Revert "Keep the globe on the same line #230"

This reverts commit ae78e5e459.

* Migrate templates from hypview: delete, edit, start empty and existing WIP

The delete media view was removed, I didn't even know it still existed as a GET. A rudiment.

* Make views multi-file and break compilation

* Megarefactoring of hypha views

* Auth-related stuffs

* Fix some of those weird imports

* Migrate cat views

* Fix cat js

* Lower standards

* Internalize trauma
2024-09-07 21:22:41 +03:00

217 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package shroom
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"mime/multipart"
"os"
"path/filepath"
"strings"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
"github.com/bouncepaw/mycorrhiza/internal/user"
)
func historyMessageForTextUpload(h hyphae.Hypha, userMessage string) string {
var verb string
switch h.(type) {
case *hyphae.EmptyHypha:
verb = "Create"
default:
verb = "Edit"
}
if userMessage == "" {
return fmt.Sprintf("%s %s", verb, h.CanonicalName())
}
return fmt.Sprintf("%s %s: %s", verb, h.CanonicalName(), userMessage)
}
func writeTextToDisk(h hyphae.ExistingHypha, data []byte, hop *history.Op) error {
if err := hyphae.WriteToMycoFile(h, data); err != nil {
return err
}
hop.WithFiles(h.TextFilePath())
return nil
}
// 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) error {
hop := history.
Operation(history.TypeEditText).
WithMsg(historyMessageForTextUpload(h, userMessage)).
WithUser(u)
// Privilege check
if !u.CanProceed("upload-text") {
rejectEditLog(h, u, "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.
hop.Abort()
return errors.New("invalid hypha name")
}
oldText, err := hyphae.FetchMycomarkupFile(h)
if err != nil {
hop.Abort()
return err
}
// Empty data check
if len(bytes.TrimSpace(data)) == 0 && len(oldText) == 0 { // if nothing but whitespace
hop.Abort()
return nil
}
// At this point, we have a savable user-generated Mycomarkup document. Gotta save it.
switch h := h.(type) {
case *hyphae.EmptyHypha:
parts := []string{files.HyphaeDir()}
parts = append(parts, strings.Split(h.CanonicalName()+".myco", "\\")...)
H := hyphae.ExtendEmptyToTextual(h, filepath.Join(parts...))
err := writeTextToDisk(H, data, hop)
if err != nil {
hop.Abort()
return err
}
hyphae.Insert(H)
backlinks.UpdateBacklinksAfterEdit(H, "")
case *hyphae.MediaHypha:
// TODO: that []byte(...) part should be removed
if bytes.Equal(data, []byte(oldText)) {
// No changes! Just like cancel button
hop.Abort()
return nil
}
err = writeTextToDisk(h, data, hop)
if err != nil {
hop.Abort()
return err
}
backlinks.UpdateBacklinksAfterEdit(h, oldText)
case *hyphae.TextualHypha:
oldText, err := hyphae.FetchMycomarkupFile(h)
if err != nil {
hop.Abort()
return err
}
// TODO: that []byte(...) part should be removed
if bytes.Equal(data, []byte(oldText)) {
// No changes! Just like cancel button
hop.Abort()
return nil
}
err = writeTextToDisk(h, data, hop)
if err != nil {
hop.Abort()
return err
}
backlinks.UpdateBacklinksAfterEdit(h, oldText)
}
hop.Apply()
return nil
}
func historyMessageForMediaUpload(h hyphae.Hypha, mime string) string {
return fmt.Sprintf("Upload media for %s with type %s", h.CanonicalName(), mime)
}
// writeMediaToDisk saves the given data with the given mime type for the given hypha to the disk and returns the path to the saved file and an error, if any.
func writeMediaToDisk(h hyphae.Hypha, mime string, data []byte) (string, error) {
var (
ext = mimetype.ToExtension(mime)
// That's where the file will go
uploadedFilePath = filepath.Join(append([]string{files.HyphaeDir()}, strings.Split(h.CanonicalName()+ext, "\\")...)...)
)
if err := os.MkdirAll(filepath.Dir(uploadedFilePath), 0777); err != nil {
return uploadedFilePath, err
}
if err := os.WriteFile(uploadedFilePath, data, 0666); err != nil {
return uploadedFilePath, err
}
return uploadedFilePath, nil
}
// 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) error {
// Privilege check
if !u.CanProceed("upload-binary") {
rejectUploadMediaLog(h, u, "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.
return errors.New("invalid hypha name")
}
data, err := io.ReadAll(file)
if err != nil {
return err
}
// Empty data check
if len(data) == 0 {
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 err
}
switch h := h.(type) {
case *hyphae.EmptyHypha:
H := hyphae.ExtendEmptyToMedia(h, uploadedFilePath)
hyphae.Insert(H)
case *hyphae.TextualHypha:
hyphae.Insert(hyphae.ExtendTextualToMedia(h, uploadedFilePath))
case *hyphae.MediaHypha: // If this is not the first media the hypha gets
prevFilePath := h.MediaFilePath()
if prevFilePath != uploadedFilePath {
if err := history.Rename(prevFilePath, uploadedFilePath); err != nil {
return err
}
log.Printf("Move %s to %s\n", prevFilePath, uploadedFilePath)
h.SetMediaFilePath(uploadedFilePath)
}
}
history.
Operation(history.TypeEditBinary).
WithMsg(historyMessageForMediaUpload(h, mime)).
WithUser(u).
WithFiles(uploadedFilePath).
Apply()
return nil
}