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:
parent
62ff0f0c01
commit
756c4e8125
@ -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", "--"}
|
||||
|
@ -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
|
||||
}
|
||||
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
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
74
hypha.go
74
hypha.go
@ -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)
|
||||
if !skip {
|
||||
var (
|
||||
hyphaPartPath = filepath.Join(path, node.Name())
|
||||
hyphaName, isText, skip = DataFromFilename(hyphaPartPath)
|
||||
hyphaData *HyphaData
|
||||
ok bool
|
||||
)
|
||||
if hyphaData, ok = HyphaStorage[hyphaName]; !ok {
|
||||
if !skip {
|
||||
// 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
45
mime.go
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user