mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-12 05:20:26 +00:00
Reimplement action=raw and action=getBinary
This commit is contained in:
parent
753d63c70a
commit
8f2515b37a
@ -8,6 +8,13 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
HyphaPattern = `[^\s\d:/?&\\][^:?&\\]*`
|
||||
HyphaUrl = `/{hypha:` + HyphaPattern + `}`
|
||||
RevisionPattern = `[\d]+`
|
||||
RevQuery = `{rev:` + RevisionPattern + `}`
|
||||
)
|
||||
|
||||
var (
|
||||
WikiDir string
|
||||
TemplatesDir string
|
||||
|
60
fs/fs.go
Normal file
60
fs/fs.go
Normal file
@ -0,0 +1,60 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
// hypha name => path
|
||||
paths map[string]string
|
||||
root string
|
||||
}
|
||||
|
||||
// InitStorage initiates filesystem-based hypha storage. It has to be called after configuration was inited.
|
||||
func InitStorage() *Storage {
|
||||
s := &Storage{
|
||||
paths: make(map[string]string),
|
||||
root: cfg.WikiDir,
|
||||
}
|
||||
s.indexHyphae(s.root)
|
||||
log.Println(s.paths)
|
||||
log.Printf("Indexed %v hyphae\n", len(s.paths))
|
||||
return s
|
||||
}
|
||||
|
||||
// hyphaName gets name of a hypha by stripping path to the hypha in `fullPath`
|
||||
func hyphaName(fullPath string) string {
|
||||
// {cfg.WikiDir}/{the name}
|
||||
return fullPath[len(cfg.WikiDir)+1:]
|
||||
}
|
||||
|
||||
// indexHyphae searches for all hyphae that seem valid in `path` and saves their absolute paths to `s.paths`. This function is recursive.
|
||||
func (s *Storage) indexHyphae(path string) {
|
||||
nodes, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Fatal("Error when checking", path, ":", err, "; skipping")
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
matchesHypha, err := regexp.MatchString(cfg.HyphaPattern, node.Name())
|
||||
if err != nil {
|
||||
log.Fatal("Error when matching", node.Name(), err, "\n")
|
||||
return
|
||||
}
|
||||
switch name := filepath.Join(path, node.Name()); {
|
||||
case matchesHypha && node.IsDir():
|
||||
s.indexHyphae(name)
|
||||
case node.Name() == "meta.json" && !node.IsDir():
|
||||
log.Printf("%v seems to be a hypha, adding it to the list\n", path)
|
||||
s.paths[hyphaName(path)] = path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hypha) Close() {
|
||||
}
|
137
fs/hypha.go
Normal file
137
fs/hypha.go
Normal file
@ -0,0 +1,137 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
)
|
||||
|
||||
type Hypha struct {
|
||||
Exists bool `json:"-"`
|
||||
FullName string `json:"-"`
|
||||
ViewCount int `json:"views"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Revisions map[string]*Revision `json:"revisions"`
|
||||
actual *Revision `json:"-"`
|
||||
}
|
||||
|
||||
func (s *Storage) Open(name string) (*Hypha, error) {
|
||||
h := &Hypha{
|
||||
Exists: true,
|
||||
FullName: name,
|
||||
}
|
||||
path, ok := s.paths[name]
|
||||
// This hypha does not exist yet
|
||||
if !ok {
|
||||
log.Println("Hypha", name, "does not exist")
|
||||
h.Exists = false
|
||||
h.Revisions = make(map[string]*Revision)
|
||||
} else {
|
||||
metaJsonText, err := ioutil.ReadFile(filepath.Join(path, "meta.json"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(metaJsonText, &h)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
// fill in rooted paths to content files and full names
|
||||
for idStr, rev := range h.Revisions {
|
||||
rev.FullName = filepath.Join(h.parentName(), rev.ShortName)
|
||||
rev.Id, _ = strconv.Atoi(idStr)
|
||||
if rev.BinaryName != "" {
|
||||
rev.BinaryPath = filepath.Join(path, rev.BinaryName)
|
||||
}
|
||||
rev.TextPath = filepath.Join(path, rev.TextName)
|
||||
}
|
||||
|
||||
err = h.OnRevision("0")
|
||||
return h, err
|
||||
}
|
||||
log.Println(h)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (h *Hypha) parentName() string {
|
||||
return filepath.Dir(h.FullName)
|
||||
}
|
||||
|
||||
func (h *Hypha) metaJsonPath() string {
|
||||
return filepath.Join(cfg.WikiDir, h.FullName, "meta.json")
|
||||
}
|
||||
|
||||
// OnRevision tries to change to a revision specified by `id`.
|
||||
func (h *Hypha) OnRevision(id string) error {
|
||||
if len(h.Revisions) == 0 {
|
||||
return errors.New("This hypha has no revisions")
|
||||
}
|
||||
if id == "0" {
|
||||
id = h.NewestId()
|
||||
}
|
||||
// Revision must be there, so no error checking
|
||||
if rev, _ := h.Revisions[id]; true {
|
||||
h.actual = rev
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewestId finds the largest id among all revisions.
|
||||
func (h *Hypha) NewestId() string {
|
||||
var largest int
|
||||
for k, _ := range h.Revisions {
|
||||
id, _ := strconv.Atoi(k)
|
||||
if id > largest {
|
||||
largest = id
|
||||
}
|
||||
}
|
||||
return strconv.Itoa(largest)
|
||||
}
|
||||
|
||||
func (h *Hypha) PlainLog(s string) {
|
||||
log.Println(h.FullName, h.actual.Id, s)
|
||||
}
|
||||
|
||||
func (h *Hypha) mimeTypeForActionRaw() string {
|
||||
// If text mime type is text/html, it is not good as it will be rendered.
|
||||
if h.actual.TextMime == "text/html" {
|
||||
return "text/plain"
|
||||
}
|
||||
return h.actual.TextMime
|
||||
}
|
||||
|
||||
// ActionRaw is used with `?action=raw`.
|
||||
// It writes text content of the revision without any parsing or rendering.
|
||||
func (h *Hypha) ActionRaw(w http.ResponseWriter) {
|
||||
fileContents, err := ioutil.ReadFile(h.actual.TextPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", h.mimeTypeForActionRaw())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fileContents)
|
||||
h.PlainLog("Serving raw text")
|
||||
}
|
||||
|
||||
// ActionGetBinary is used with `?action=getBinary`.
|
||||
// It writes contents of binary content file.
|
||||
func (h *Hypha) ActionGetBinary(w http.ResponseWriter) {
|
||||
fileContents, err := ioutil.ReadFile(h.actual.BinaryPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", h.actual.BinaryMime)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fileContents)
|
||||
h.PlainLog("Serving raw text")
|
||||
}
|
17
fs/revision.go
Normal file
17
fs/revision.go
Normal file
@ -0,0 +1,17 @@
|
||||
package fs
|
||||
|
||||
type Revision struct {
|
||||
Id int `json:"-"`
|
||||
FullName string `json:"-"`
|
||||
Tags []string `json:"tags"`
|
||||
ShortName string `json:"name"`
|
||||
Comment string `json:"comment"`
|
||||
Author string `json:"author"`
|
||||
Time int `json:"time"`
|
||||
TextMime string `json:"text_mime"`
|
||||
BinaryMime string `json:"binary_mime"`
|
||||
TextPath string `json:"-"`
|
||||
BinaryPath string `json:"-"`
|
||||
TextName string `json:"text_name"`
|
||||
BinaryName string `json:"binary_name"`
|
||||
}
|
71
handlers.go
71
handlers.go
@ -1,65 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
// "io/ioutil"
|
||||
// "log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
// "path/filepath"
|
||||
// "strconv"
|
||||
// "strings"
|
||||
// "time"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
"github.com/bouncepaw/mycorrhiza/fs"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// There are handlers below. See main() for their usage.
|
||||
|
||||
// Boilerplate code present in many handlers. Good to have it.
|
||||
func HandlerBase(w http.ResponseWriter, rq *http.Request) (Revision, bool) {
|
||||
func HandlerBase(w http.ResponseWriter, rq *http.Request) (*fs.Hypha, bool) {
|
||||
vars := mux.Vars(rq)
|
||||
revno := RevInMap(vars)
|
||||
return GetRevision(vars["hypha"], revno)
|
||||
}
|
||||
|
||||
func HandlerGetBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
if rev, ok := HandlerBase(w, rq); ok {
|
||||
rev.ActionGetBinary(w)
|
||||
h, err := hs.Open(vars["hypha"])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, false
|
||||
}
|
||||
err = h.OnRevision(RevInMap(vars))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
log.Println(*h)
|
||||
return h, true
|
||||
}
|
||||
|
||||
func HandlerRaw(w http.ResponseWriter, rq *http.Request) {
|
||||
if rev, ok := HandlerBase(w, rq); ok {
|
||||
rev.ActionRaw(w)
|
||||
log.Println("?action=raw")
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionRaw(w)
|
||||
}
|
||||
}
|
||||
|
||||
func HandlerGetBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("?action=getBinary")
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionGetBinary(w)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func HandlerZen(w http.ResponseWriter, rq *http.Request) {
|
||||
if rev, ok := HandlerBase(w, rq); ok {
|
||||
rev.ActionZen(w)
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionZen(w)
|
||||
}
|
||||
}
|
||||
|
||||
func HandlerView(w http.ResponseWriter, rq *http.Request) {
|
||||
if rev, ok := HandlerBase(w, rq); ok {
|
||||
rev.ActionView(w, HyphaPage)
|
||||
} else { // Hypha does not exist
|
||||
log.Println("Hypha does not exist, showing 404")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(Hypha404(mux.Vars(rq)["hypha"])))
|
||||
if h, ok := HandlerBase(w, rq); ok {
|
||||
h.ActionView(w, HyphaPage)
|
||||
}
|
||||
}
|
||||
|
||||
func HandlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
}
|
||||
|
||||
func HandlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||
vars := mux.Vars(rq)
|
||||
ActionEdit(vars["hypha"], w)
|
||||
}
|
||||
|
||||
func HandlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
}
|
||||
|
||||
func HandlerRewind(w http.ResponseWriter, rq *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
@ -74,7 +84,9 @@ func HandlerRename(w http.ResponseWriter, rq *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
log.Println("Attempt to access an unimplemented thing")
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// makeTagsSlice turns strings like `"foo,, bar,kek"` to slice of strings that represent tag names. Whitespace around commas is insignificant.
|
||||
// Expected output for string above: []string{"foo", "bar", "kek"}
|
||||
func makeTagsSlice(responseTagsString string) (ret []string) {
|
||||
@ -211,3 +223,4 @@ func HandlerUpdate(w http.ResponseWriter, rq *http.Request) {
|
||||
d := map[string]string{"Name": h.FullName}
|
||||
w.Write([]byte(renderFromMap(d, "updateOk.html")))
|
||||
}
|
||||
*/
|
||||
|
@ -14,18 +14,6 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
)
|
||||
|
||||
// `Hypha` represents a hypha. It is the thing MycorrhizaWiki generally serves.
|
||||
// Each hypha has 1 or more revisions.
|
||||
type Hypha struct {
|
||||
FullName string `json:"-"`
|
||||
Path string `json:"-"`
|
||||
ViewCount int `json:"views"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Revisions map[string]*Revision `json:"revisions"`
|
||||
ChildrenNames []string `json:"-"`
|
||||
parentName string
|
||||
}
|
||||
|
||||
// AsHtml returns HTML representation of the hypha.
|
||||
// No layout or navigation are present here. Just the hypha.
|
||||
func (h *Hypha) AsHtml(id string, w http.ResponseWriter) (string, error) {
|
||||
@ -38,33 +26,6 @@ func (h *Hypha) AsHtml(id string, w http.ResponseWriter) (string, error) {
|
||||
return "", fmt.Errorf("Hypha %v has no such revision: %v", h.FullName, id)
|
||||
}
|
||||
|
||||
// GetNewestRevision returns the most recent Revision.
|
||||
func (h *Hypha) GetNewestRevision() Revision {
|
||||
return *h.Revisions[h.NewestRevision()]
|
||||
}
|
||||
|
||||
// NewestRevision returns the most recent revision's id as a string.
|
||||
func (h *Hypha) NewestRevision() string {
|
||||
return strconv.Itoa(h.NewestRevisionInt())
|
||||
}
|
||||
|
||||
// NewestRevision returns the most recent revision's id as an integer.
|
||||
func (h *Hypha) NewestRevisionInt() (ret int) {
|
||||
for k, _ := range h.Revisions {
|
||||
id, _ := strconv.Atoi(k)
|
||||
if id > ret {
|
||||
ret = id
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// MetaJsonPath returns rooted path to the hypha's `meta.json` file.
|
||||
// It is not promised that the file exists.
|
||||
func (h *Hypha) MetaJsonPath() string {
|
||||
return filepath.Join(h.Path, "meta.json")
|
||||
}
|
||||
|
||||
// CreateDir creates directory where the hypha must reside.
|
||||
// It is meant to be used with new hyphae.
|
||||
func (h *Hypha) CreateDir() error {
|
88
main.go
88
main.go
@ -9,25 +9,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
"github.com/bouncepaw/mycorrhiza/fs"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// GetRevision finds revision with id `id` of `hyphaName` in `hyphae`.
|
||||
// If `id` is `"0"`, it means the last revision.
|
||||
// If no such revision is found, last return value is false.
|
||||
func GetRevision(hyphaName string, id string) (Revision, bool) {
|
||||
log.Println("Getting hypha", hyphaName, id)
|
||||
if hypha, ok := hyphae[hyphaName]; ok {
|
||||
if id == "0" {
|
||||
id = hypha.NewestRevision()
|
||||
}
|
||||
if rev, ok := hypha.Revisions[id]; ok {
|
||||
return *rev, true
|
||||
}
|
||||
}
|
||||
return Revision{}, false
|
||||
}
|
||||
|
||||
// RevInMap finds value of `rev` (the one from URL queries like) in the passed map that is usually got from `mux.Vars(*http.Request)`.
|
||||
// If there is no `rev`, return "0".
|
||||
func RevInMap(m map[string]string) string {
|
||||
@ -37,8 +22,7 @@ func RevInMap(m map[string]string) string {
|
||||
return "0"
|
||||
}
|
||||
|
||||
// `hyphae` is a map with all hyphae. Many functions use it.
|
||||
var hyphae map[string]*Hypha
|
||||
var hs *fs.Storage
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 1 {
|
||||
@ -52,51 +36,51 @@ func main() {
|
||||
log.Println("Welcome to MycorrhizaWiki α")
|
||||
cfg.InitConfig(wikiDir)
|
||||
log.Println("Indexing hyphae...")
|
||||
hyphae = recurFindHyphae(wikiDir)
|
||||
log.Println("Indexed", len(hyphae), "hyphae. Ready to accept requests.")
|
||||
hs = fs.InitStorage()
|
||||
|
||||
// Start server code. See handlers.go for handlers' implementations.
|
||||
r := mux.NewRouter()
|
||||
|
||||
r.Queries("action", "getBinary", "rev", revQuery).Path(hyphaUrl).
|
||||
r.Queries("action", "getBinary", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
|
||||
HandlerFunc(HandlerGetBinary)
|
||||
r.Queries("action", "getBinary").Path(hyphaUrl).
|
||||
r.Queries("action", "getBinary").Path(cfg.HyphaUrl).
|
||||
HandlerFunc(HandlerGetBinary)
|
||||
|
||||
r.Queries("action", "raw", "rev", revQuery).Path(hyphaUrl).
|
||||
r.Queries("action", "raw", "rev", cfg.RevQuery).Path(cfg.HyphaUrl).
|
||||
HandlerFunc(HandlerRaw)
|
||||
r.Queries("action", "raw").Path(hyphaUrl).
|
||||
r.Queries("action", "raw").Path(cfg.HyphaUrl).
|
||||
HandlerFunc(HandlerRaw)
|
||||
/*
|
||||
r.Queries("action", "zen", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerZen)
|
||||
r.Queries("action", "zen").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerZen)
|
||||
|
||||
r.Queries("action", "zen", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerZen)
|
||||
r.Queries("action", "zen").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerZen)
|
||||
r.Queries("action", "view", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerView)
|
||||
r.Queries("action", "view").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerView)
|
||||
|
||||
r.Queries("action", "view", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerView)
|
||||
r.Queries("action", "view").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerView)
|
||||
r.Queries("action", "history").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerHistory)
|
||||
|
||||
r.Queries("action", "history").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerHistory)
|
||||
r.Queries("action", "edit").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerEdit)
|
||||
|
||||
r.Queries("action", "edit").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerEdit)
|
||||
r.Queries("action", "rewind", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerRewind)
|
||||
|
||||
r.Queries("action", "rewind", "rev", revQuery).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerRewind)
|
||||
r.Queries("action", "delete").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerDelete)
|
||||
|
||||
r.Queries("action", "delete").Path(hyphaUrl).
|
||||
HandlerFunc(HandlerDelete)
|
||||
r.Queries("action", "rename", "to", hyphaPattern).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerRename)
|
||||
|
||||
r.Queries("action", "rename", "to", hyphaPattern).Path(hyphaUrl).
|
||||
HandlerFunc(HandlerRename)
|
||||
r.Queries("action", "update").Path(hyphaUrl).Methods("POST").
|
||||
HandlerFunc(HandlerUpdate)
|
||||
*/
|
||||
|
||||
r.Queries("action", "update").Path(hyphaUrl).Methods("POST").
|
||||
HandlerFunc(HandlerUpdate)
|
||||
|
||||
r.HandleFunc(hyphaUrl, HandlerView)
|
||||
// r.HandleFunc(hyphaUrl, HandlerView)
|
||||
|
||||
// Debug page that renders all hyphae.
|
||||
// TODO: make it redirect to home page.
|
||||
@ -104,14 +88,10 @@ func main() {
|
||||
r.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
for _, h := range hyphae {
|
||||
log.Println("Rendering latest revision of hypha", h.FullName)
|
||||
html, err := h.AsHtml("0", w)
|
||||
if err != nil {
|
||||
fmt.Fprintln(w, err)
|
||||
}
|
||||
fmt.Fprintln(w, html)
|
||||
}
|
||||
fmt.Fprintln(w, `
|
||||
<p>Check out <a href="/Fruit">Fruit</a> maybe.</p>
|
||||
<p><strong>TODO:</strong> make this page usable</p>
|
||||
`)
|
||||
})
|
||||
|
||||
http.Handle("/", r)
|
||||
|
@ -114,19 +114,6 @@ func (rev *Revision) ActionGetBinary(w http.ResponseWriter) {
|
||||
log.Println("Serving binary data of", rev.FullName, rev.Id)
|
||||
}
|
||||
|
||||
// ActionRaw is used with `?action=raw`.
|
||||
// It writes text content of the revision without any parsing or rendering.
|
||||
func (rev *Revision) ActionRaw(w http.ResponseWriter) {
|
||||
fileContents, err := ioutil.ReadFile(rev.TextPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", rev.TextMime)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fileContents)
|
||||
log.Println("Serving text data of", rev.FullName, rev.Id)
|
||||
}
|
||||
|
||||
// ActionZen is used with `?action=zen`.
|
||||
// It renders the revision without any layout or navigation.
|
||||
func (rev *Revision) ActionZen(w http.ResponseWriter) {
|
1
w/m/Fruit/Pineapple/1.html
Normal file
1
w/m/Fruit/Pineapple/1.html
Normal file
@ -0,0 +1 @@
|
||||
<b>Pineapple is apple from a pine</b>
|
19
w/m/Fruit/Pineapple/meta.json
Normal file
19
w/m/Fruit/Pineapple/meta.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"views": 0,
|
||||
"deleted": false,
|
||||
"revisions": {
|
||||
"1": {
|
||||
"tags": [
|
||||
""
|
||||
],
|
||||
"name": "Pineapple",
|
||||
"comment": "Update Fruit/Pineapple",
|
||||
"author": "",
|
||||
"time": 1592997100,
|
||||
"text_mime": "text/html",
|
||||
"binary_mime": "",
|
||||
"text_name": "1.html",
|
||||
"binary_name": ""
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user