1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-18 22:52:50 +00:00

Start implementing new wiki file structure

This commit is contained in:
bouncepaw 2020-10-22 21:42:48 +05:00
parent 62ff0f0c01
commit 756c4e8125
5 changed files with 114 additions and 108 deletions

View File

@ -55,6 +55,12 @@ func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
return hop
}
// WithError appends the `err` to the list of errors.
func (hop *HistoryOp) WithError(err error) *HistoryOp {
hop.Errs = append(hop.Errs, err)
return hop
}
// WithFilesRemoved git-rm-s all passed `paths`. Paths can be rooted or not. Paths that are empty strings are ignored.
func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
args := []string{"rm", "--quiet", "--"}

View File

@ -2,13 +2,9 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/templates"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -116,8 +112,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
textAreaFill, err = FetchTextPart(hyphaData)
if err != nil {
log.Println(err)
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
"Could not fetch text data")
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", "Could not fetch text data")
return
}
} else {
@ -133,42 +128,16 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
hyphaName = HyphaNameFromRq(rq, "upload-text")
hyphaData, isOld = HyphaStorage[hyphaName]
textData = rq.PostFormValue("text")
textDataBytes = []byte(textData)
fullPath = filepath.Join(WikiDir, hyphaName+"&.gmi")
)
if textData == "" {
HttpErr(w, http.StatusBadRequest, hyphaName, "Error",
"No text data passed")
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No text data passed")
return
}
// For some reason, only 0777 works. Why?
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
log.Println(err)
}
if err := ioutil.WriteFile(fullPath, textDataBytes, 0644); err != nil {
log.Println(err)
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
fmt.Sprintf("Failed to write %d bytes to %s",
len(textDataBytes), fullPath))
return
}
if !isOld {
hd := HyphaData{
textType: TextGemini,
textPath: fullPath,
}
HyphaStorage[hyphaName] = &hd
hyphaData = &hd
if hop := hyphaData.UploadText(hyphaName, textData, isOld); len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
} else {
hyphaData.textType = TextGemini
hyphaData.textPath = fullPath
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}
history.Operation(history.TypeEditText).
WithFiles(fullPath).
WithMsg(fmt.Sprintf("Edit %s", hyphaName)).
WithSignature("anon").
Apply()
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}
// handlerUploadBinary uploads a new binary part for the hypha.
@ -176,62 +145,26 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
hyphaName := HyphaNameFromRq(rq, "upload-binary")
rq.ParseMultipartForm(10 << 20)
// Read file
file, handler, err := rq.FormFile("binary")
if file != nil {
defer file.Close()
}
// If file is not passed:
if err != nil {
HttpErr(w, http.StatusBadRequest, hyphaName, "Error",
"No binary data passed")
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No binary data passed")
return
}
// If file is passed:
var (
hyphaData, isOld = HyphaStorage[hyphaName]
mimeType = MimeToBinaryType(handler.Header.Get("Content-Type"))
ext = mimeType.Extension()
fullPath = filepath.Join(WikiDir, hyphaName+"&"+ext)
mime = handler.Header.Get("Content-Type")
hop = hyphaData.UploadBinary(hyphaName, mime, file, isOld)
)
data, err := ioutil.ReadAll(file)
if err != nil {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
"Could not read passed data")
return
}
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
log.Println(err)
}
if !isOld {
hd := HyphaData{
binaryPath: fullPath,
binaryType: mimeType,
}
HyphaStorage[hyphaName] = &hd
hyphaData = &hd
if len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
} else {
if hyphaData.binaryPath != fullPath {
if err := history.Rename(hyphaData.binaryPath, fullPath); err != nil {
log.Println(err)
} else {
log.Println("Moved", hyphaData.binaryPath, "to", fullPath)
}
}
hyphaData.binaryPath = fullPath
hyphaData.binaryType = mimeType
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}
if err = ioutil.WriteFile(fullPath, data, 0644); err != nil {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error",
"Could not save passed data")
return
}
log.Println("Written", len(data), "of binary data for", hyphaName, "to path", fullPath)
history.Operation(history.TypeEditText).
WithFiles(fullPath, hyphaData.binaryPath).
WithMsg(fmt.Sprintf("Upload binary part for %s with type %s", hyphaName, mimeType.Mime())).
WithSignature("anon").
Apply()
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"log"
"mime/multipart"
"os"
"path/filepath"
"strings"
@ -40,6 +41,52 @@ type HyphaData struct {
binaryType BinaryType
}
// uploadHelp is a helper function for UploadText and UploadBinary
func (hd *HyphaData) uploadHelp(hop *history.HistoryOp, hyphaName, ext, originalFullPath string, isOld bool, data []byte) *history.HistoryOp {
var (
fullPath = filepath.Join(WikiDir, hyphaName+ext)
)
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
return hop.WithError(err)
}
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
return hop.WithError(err)
}
if isOld && originalFullPath != fullPath {
if err := history.Rename(originalFullPath, fullPath); err != nil {
return hop.WithError(err)
}
log.Println("Move", originalFullPath, "to", fullPath)
}
if !isOld {
HyphaStorage[hyphaName] = hd
}
return hop.WithFiles(fullPath).
WithSignature("anon").
Apply()
}
// UploadText loads a new text part from `textData` for hypha `hyphaName` with `hd`. It must be marked if the hypha `isOld`.
func (hd *HyphaData) UploadText(hyphaName, textData string, isOld bool) *history.HistoryOp {
hop := history.Operation(history.TypeEditText).WithMsg(fmt.Sprintf("Edit %s", hyphaName))
return hd.uploadHelp(hop, hyphaName, ".myco", hd.textPath, isOld, []byte(textData))
}
// UploadBinary loads a new binary part from `file` for hypha `hyphaName` with `hd`. The contents have the specified `mime` type. It must be marked if the hypha `isOld`.
func (hd *HyphaData) UploadBinary(hyphaName, mime string, file multipart.File, isOld bool) *history.HistoryOp {
var (
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for %s with type %s", hyphaName, mime))
data, err = ioutil.ReadAll(file)
)
if err != nil {
return hop.WithError(err)
}
return hd.uploadHelp(hop, hyphaName, MimeToExtension(mime), hd.binaryPath, isOld, data)
}
// DeleteHypha deletes hypha and makes a history record about that.
func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp {
hop := history.Operation(history.TypeDeleteHypha).
@ -160,30 +207,35 @@ func Index(path string) {
}
for _, node := range nodes {
// If this hypha looks like it can be a hypha path, go deeper
if node.IsDir() && isCanonicalName(node.Name()) {
// If this hypha looks like it can be a hypha path, go deeper. Do not touch the .git and static folders for they have an admnistrative importance!
if node.IsDir() && isCanonicalName(node.Name()) && node.Name() != ".git" && node.Name() != "static" {
Index(filepath.Join(path, node.Name()))
}
hyphaPartFilename := filepath.Join(path, node.Name())
skip, hyphaName, isText, mimeId := DataFromFilename(hyphaPartFilename)
var (
hyphaPartPath = filepath.Join(path, node.Name())
hyphaName, isText, skip = DataFromFilename(hyphaPartPath)
hyphaData *HyphaData
)
if !skip {
var (
hyphaData *HyphaData
ok bool
)
if hyphaData, ok = HyphaStorage[hyphaName]; !ok {
// Reuse the entry for existing hyphae, create a new one for those that do not exist yet.
if hd, ok := HyphaStorage[hyphaName]; ok {
hyphaData = hd
} else {
hyphaData = &HyphaData{}
HyphaStorage[hyphaName] = hyphaData
}
if isText {
hyphaData.textPath = hyphaPartFilename
hyphaData.textType = TextType(mimeId)
hyphaData.textPath = hyphaPartPath
} else {
hyphaData.binaryPath = hyphaPartFilename
hyphaData.binaryType = BinaryType(mimeId)
// Notify the user about binary part collisions. It's a design decision to just use any of them, it's the user's fault that they have screwed up the folder structure, but the engine should at least let them know, right?
if hyphaData.binaryPath != "" {
log.Println("There is a file collision for binary part of a hypha:", hyphaData.binaryPath, "and", hyphaPartPath, "-- going on with the latter")
}
hyphaData.binaryPath = hyphaPartPath
}
}
}
}

@ -1 +1 @@
Subproject commit 2c0e43199ed28f7022a38463a0eec3af3ecb03c9
Subproject commit faab70f77e18197b1c4cecb5ad54e6d26da843f5

45
mime.go
View File

@ -44,12 +44,7 @@ const (
BinaryMp4
)
var binaryMimes = [...]string{
"application/octet-stream",
"image/jpeg", "image/gif", "image/png", "image/webp",
"image/svg+xml", "image/x-icon",
"application/ogg", "video/webm", "audio/mp3", "video/mp4",
}
var binaryMimes = [...]string{}
// Mime returns mime type representation of `t`.
func (t BinaryType) Mime() string {
@ -66,6 +61,26 @@ func (t BinaryType) Extension() string {
return binaryExtensions[t]
}
func MimeToExtension(mime string) string {
mm := map[string]string{
"application/octet-stream": "bin",
"image/jpeg": "jpg",
"image/gif": "gif",
"image/png": "png",
"image/webp": "webp",
"image/svg+xml": "svg",
"image/x-icon": "ico",
"application/ogg": "ogg",
"video/webm": "webm",
"audio/mp3": "mp3",
"video/mp4": "mp4",
}
if ext, ok := mm[mime]; ok {
return "." + ext
}
return ".bin"
}
// MimeToBinaryType converts mime type to BinaryType. If the mime type is not supported, BinaryOctet is returned as a fallback type.
func MimeToBinaryType(mime string) BinaryType {
for i, binaryMime := range binaryMimes {
@ -77,17 +92,17 @@ func MimeToBinaryType(mime string) BinaryType {
}
// DataFromFilename fetches all meta information from hypha content file with path `fullPath`. If it is not a content file, `skip` is true, and you are expected to ignore this file when indexing hyphae. `name` is name of the hypha to which this file relates. `isText` is true when the content file is text, false when is binary. `mimeId` is an integer representation of content type. Cast it to TextType if `isText == true`, cast it to BinaryType if `isText == false`.
func DataFromFilename(fullPath string) (skip bool, name string, isText bool, mimeId int) {
func DataFromFilename(fullPath string) (name string, isText bool, skip bool) {
shortPath := strings.TrimPrefix(fullPath, WikiDir)[1:]
// Special files start with &
// &. is used in normal hypha part names
if shortPath[0] == '&' || strings.LastIndex(shortPath, "&.") < 0 {
skip = true
return
}
ext := filepath.Ext(shortPath)
name = strings.TrimSuffix(shortPath, "&"+ext)
isText, mimeId = mimeData(ext)
name = CanonicalName(strings.TrimSuffix(shortPath, ext))
switch ext {
case ".myco":
isText = true
case "", shortPath:
skip = true
}
return
}