2021-05-09 10:42:12 +00:00
|
|
|
|
package web
|
2020-08-05 15:08:59 +00:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2021-09-23 09:36:54 +00:00
|
|
|
|
"io"
|
2020-08-05 15:08:59 +00:00
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
2020-10-22 17:12:12 +00:00
|
|
|
|
"path/filepath"
|
2020-08-20 17:20:13 +00:00
|
|
|
|
"strings"
|
2020-08-05 15:08:59 +00:00
|
|
|
|
|
2021-07-15 17:46:35 +00:00
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
|
2021-09-12 08:58:03 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
2020-08-19 18:54:23 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/history"
|
2021-02-17 18:41:35 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/hyphae"
|
2021-09-06 17:46:34 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/l18n"
|
2021-01-28 19:07:21 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/mimetype"
|
2021-01-24 07:30:14 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/user"
|
2020-08-31 17:52:26 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/util"
|
2021-02-20 15:48:51 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/views"
|
2021-05-25 07:11:16 +00:00
|
|
|
|
|
2021-10-05 20:10:28 +00:00
|
|
|
|
"github.com/bouncepaw/mycomarkup/v3"
|
|
|
|
|
"github.com/bouncepaw/mycomarkup/v3/mycocontext"
|
|
|
|
|
"github.com/bouncepaw/mycomarkup/v3/tools"
|
2020-08-05 15:08:59 +00:00
|
|
|
|
)
|
|
|
|
|
|
2021-07-15 17:46:35 +00:00
|
|
|
|
func initReaders(r *mux.Router) {
|
|
|
|
|
r.PathPrefix("/page/").HandlerFunc(handlerHypha)
|
|
|
|
|
r.PathPrefix("/hypha/").HandlerFunc(handlerHypha)
|
|
|
|
|
r.PathPrefix("/text/").HandlerFunc(handlerText)
|
|
|
|
|
r.PathPrefix("/binary/").HandlerFunc(handlerBinary)
|
|
|
|
|
r.PathPrefix("/rev/").HandlerFunc(handlerRevision)
|
2021-09-23 09:36:54 +00:00
|
|
|
|
r.PathPrefix("/rev-text/").HandlerFunc(handlerRevisionText)
|
2021-07-15 17:46:35 +00:00
|
|
|
|
r.PathPrefix("/primitive-diff/").HandlerFunc(handlerPrimitiveDiff)
|
2022-02-26 07:33:09 +00:00
|
|
|
|
r.PathPrefix("/media/").HandlerFunc(handlerMedia)
|
2021-02-24 17:34:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-26 07:33:09 +00:00
|
|
|
|
func handlerMedia(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
2021-02-24 17:34:42 +00:00
|
|
|
|
var (
|
2022-02-26 07:33:09 +00:00
|
|
|
|
hyphaName = util.HyphaNameFromRq(rq, "media")
|
2021-02-24 17:34:42 +00:00
|
|
|
|
h = hyphae.ByName(hyphaName)
|
|
|
|
|
u = user.FromRequest(rq)
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc = l18n.FromRequest(rq)
|
2021-02-24 17:34:42 +00:00
|
|
|
|
)
|
|
|
|
|
util.HTTP200Page(w,
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.Base(
|
2022-02-26 07:33:09 +00:00
|
|
|
|
lc.Get("ui.media_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName)}),
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.MediaMenu(rq, h, u),
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc,
|
2021-02-24 17:34:42 +00:00
|
|
|
|
u))
|
2020-08-20 17:20:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-14 15:01:32 +00:00
|
|
|
|
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
2021-03-14 15:01:32 +00:00
|
|
|
|
var (
|
2021-10-01 17:12:16 +00:00
|
|
|
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
|
|
|
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
|
|
|
|
revHash = shorterURL[:firstSlashIndex]
|
|
|
|
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
2021-03-14 15:01:32 +00:00
|
|
|
|
h = hyphae.ByName(hyphaName)
|
|
|
|
|
u = user.FromRequest(rq)
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc = l18n.FromRequest(rq)
|
2021-03-14 15:01:32 +00:00
|
|
|
|
)
|
2022-02-19 08:26:38 +00:00
|
|
|
|
switch h := h.(type) {
|
|
|
|
|
case *hyphae.EmptyHypha:
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
_, _ = io.WriteString(w, "404 not found")
|
|
|
|
|
case hyphae.ExistingHypha:
|
|
|
|
|
util.HTTP200Page(w,
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.Base(
|
2022-02-19 08:26:38 +00:00
|
|
|
|
lc.Get("ui.diff_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.PrimitiveDiff(rq, h, u, revHash),
|
2022-02-19 08:26:38 +00:00
|
|
|
|
lc,
|
|
|
|
|
u))
|
|
|
|
|
}
|
2021-03-14 15:01:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 09:36:54 +00:00
|
|
|
|
// handlerRevisionText sends Mycomarkup text of the hypha at the given revision. See also: handlerRevision, handlerText.
|
|
|
|
|
//
|
|
|
|
|
// /rev-text/<revHash>/<hyphaName>
|
|
|
|
|
func handlerRevisionText(w http.ResponseWriter, rq *http.Request) {
|
|
|
|
|
util.PrepareRq(rq)
|
|
|
|
|
var (
|
2022-02-19 08:26:38 +00:00
|
|
|
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/rev-text/")
|
|
|
|
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
|
|
|
|
revHash = shorterURL[:firstSlashIndex]
|
|
|
|
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
|
|
|
|
h = hyphae.ByName(hyphaName)
|
2021-09-23 09:36:54 +00:00
|
|
|
|
)
|
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
2022-02-19 08:26:38 +00:00
|
|
|
|
switch h := h.(type) {
|
|
|
|
|
case *hyphae.EmptyHypha:
|
|
|
|
|
log.Printf(`Hypha ‘%s’ does not exist`)
|
2021-09-23 09:36:54 +00:00
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
2022-02-19 08:26:38 +00:00
|
|
|
|
case hyphae.ExistingHypha:
|
|
|
|
|
if !h.HasTextFile() {
|
|
|
|
|
log.Printf(`Media hypha ‘%s’ has no text`)
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
}
|
|
|
|
|
var textContents, err = history.FileAtRevision(h.TextFilePath(), revHash)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
log.Printf("While serving text of ‘%s’ at revision ‘%s’: %s\n", hyphaName, revHash, err.Error())
|
|
|
|
|
_, _ = io.WriteString(w, "Error: "+err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
log.Printf("Serving text of ‘%s’ from ‘%s’ at revision ‘%s’\n", hyphaName, h.TextFilePath(), revHash)
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
_, _ = io.WriteString(w, textContents)
|
2021-09-23 09:36:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handlerRevision displays a specific revision of the text part the hypha
|
2020-08-20 17:20:13 +00:00
|
|
|
|
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
2020-08-20 17:20:13 +00:00
|
|
|
|
var (
|
2022-02-19 08:26:38 +00:00
|
|
|
|
lc = l18n.FromRequest(rq)
|
|
|
|
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
|
|
|
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
|
|
|
|
revHash = shorterURL[:firstSlashIndex]
|
|
|
|
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
|
|
|
|
h = hyphae.ByName(hyphaName)
|
|
|
|
|
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
|
|
|
|
|
u = user.FromRequest(rq)
|
2020-08-20 17:20:13 +00:00
|
|
|
|
)
|
2022-02-19 08:26:38 +00:00
|
|
|
|
switch h := h.(type) {
|
2022-02-19 08:29:14 +00:00
|
|
|
|
case hyphae.ExistingHypha:
|
2022-02-19 08:26:38 +00:00
|
|
|
|
var textContents, err = history.FileAtRevision(h.TextFilePath(), revHash)
|
|
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textContents)
|
|
|
|
|
contents = mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))
|
|
|
|
|
}
|
2020-08-20 17:20:13 +00:00
|
|
|
|
}
|
2022-03-20 21:24:40 +00:00
|
|
|
|
page := views.Revision(
|
2020-11-16 15:26:03 +00:00
|
|
|
|
rq,
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc,
|
2021-02-20 15:48:51 +00:00
|
|
|
|
h,
|
2020-08-20 17:20:13 +00:00
|
|
|
|
contents,
|
2020-08-31 17:52:26 +00:00
|
|
|
|
revHash,
|
|
|
|
|
)
|
2020-08-20 17:20:13 +00:00
|
|
|
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2021-08-12 12:12:53 +00:00
|
|
|
|
_, _ = fmt.Fprint(
|
|
|
|
|
w,
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.Base(
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc.Get("ui.revision_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
|
2021-08-12 12:12:53 +00:00
|
|
|
|
page,
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc,
|
2021-08-12 12:12:53 +00:00
|
|
|
|
u,
|
|
|
|
|
),
|
|
|
|
|
)
|
2020-08-19 18:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-05 15:08:59 +00:00
|
|
|
|
// handlerText serves raw source text of the hypha.
|
|
|
|
|
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
|
|
|
|
hyphaName := util.HyphaNameFromRq(rq, "text")
|
2022-02-04 14:56:28 +00:00
|
|
|
|
switch h := hyphae.ByName(hyphaName).(type) {
|
2022-02-19 08:26:38 +00:00
|
|
|
|
case *hyphae.TextualHypha:
|
|
|
|
|
log.Println("Serving", h.TextFilePath())
|
|
|
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
|
http.ServeFile(w, rq, h.TextFilePath())
|
2020-08-05 15:08:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-25 13:08:59 +00:00
|
|
|
|
// handlerBinary serves attachment of the hypha.
|
2020-08-05 15:08:59 +00:00
|
|
|
|
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
|
|
|
|
hyphaName := util.HyphaNameFromRq(rq, "binary")
|
2022-02-04 14:56:28 +00:00
|
|
|
|
switch h := hyphae.ByName(hyphaName).(type) {
|
|
|
|
|
case *hyphae.EmptyHypha:
|
2022-02-19 08:26:38 +00:00
|
|
|
|
case *hyphae.TextualHypha:
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
log.Printf("Textual hypha ‘%s’ has no media, cannot serve\n", h.CanonicalName())
|
|
|
|
|
case *hyphae.MediaHypha:
|
|
|
|
|
log.Println("Serving", h.MediaFilePath())
|
|
|
|
|
w.Header().Set("Content-Type", mimetype.FromExtension(filepath.Ext(h.MediaFilePath())))
|
|
|
|
|
http.ServeFile(w, rq, h.MediaFilePath())
|
2020-08-05 15:08:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-30 18:29:56 +00:00
|
|
|
|
// handlerHypha is the main hypha action that displays the hypha and the binary upload form along with some navigation.
|
|
|
|
|
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
|
util.PrepareRq(rq)
|
2020-08-05 15:08:59 +00:00
|
|
|
|
var (
|
2021-05-09 10:42:12 +00:00
|
|
|
|
hyphaName = util.HyphaNameFromRq(rq, "page", "hypha")
|
2021-02-17 18:41:35 +00:00
|
|
|
|
h = hyphae.ByName(hyphaName)
|
|
|
|
|
contents string
|
|
|
|
|
openGraph string
|
|
|
|
|
u = user.FromRequest(rq)
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc = l18n.FromRequest(rq)
|
2020-08-05 15:08:59 +00:00
|
|
|
|
)
|
2022-02-04 14:56:28 +00:00
|
|
|
|
|
|
|
|
|
switch h := h.(type) {
|
|
|
|
|
case *hyphae.EmptyHypha:
|
|
|
|
|
util.HTTP404Page(w,
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.Base(
|
2022-02-04 14:56:28 +00:00
|
|
|
|
util.BeautifulName(hyphaName),
|
2022-03-22 13:57:25 +00:00
|
|
|
|
views.Hypha(views.MetaFrom(w, rq), h, contents),
|
2022-02-04 14:56:28 +00:00
|
|
|
|
lc,
|
|
|
|
|
u,
|
|
|
|
|
openGraph))
|
2022-02-19 08:26:38 +00:00
|
|
|
|
case hyphae.ExistingHypha:
|
|
|
|
|
fileContentsT, errT := os.ReadFile(h.TextFilePath())
|
2020-08-05 15:08:59 +00:00
|
|
|
|
if errT == nil {
|
2021-05-25 07:11:16 +00:00
|
|
|
|
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, string(fileContentsT))
|
2021-06-12 09:42:00 +00:00
|
|
|
|
ctx = mycocontext.WithWebSiteURL(ctx, cfg.URL)
|
2021-09-12 08:58:03 +00:00
|
|
|
|
getOpenGraph, descVisitor, imgVisitor := tools.OpenGraphVisitors(ctx)
|
2021-06-12 09:42:00 +00:00
|
|
|
|
ast := mycomarkup.BlockTree(ctx, descVisitor, imgVisitor)
|
2021-05-25 07:11:16 +00:00
|
|
|
|
contents = mycomarkup.BlocksToHTML(ctx, ast)
|
2021-06-12 09:42:00 +00:00
|
|
|
|
openGraph = getOpenGraph()
|
2020-08-05 15:08:59 +00:00
|
|
|
|
}
|
2022-02-19 08:26:38 +00:00
|
|
|
|
switch h := h.(type) {
|
|
|
|
|
case *hyphae.MediaHypha:
|
2022-03-20 21:24:40 +00:00
|
|
|
|
contents = views.Media(h, lc) + contents
|
2020-08-05 15:08:59 +00:00
|
|
|
|
}
|
2022-02-04 14:56:28 +00:00
|
|
|
|
|
2021-03-05 12:20:51 +00:00
|
|
|
|
util.HTTP200Page(w,
|
2022-03-20 21:24:40 +00:00
|
|
|
|
views.Base(
|
2021-03-05 12:20:51 +00:00
|
|
|
|
util.BeautifulName(hyphaName),
|
2022-03-22 13:57:25 +00:00
|
|
|
|
views.Hypha(views.MetaFrom(w, rq), h, contents),
|
2021-09-06 17:46:34 +00:00
|
|
|
|
lc,
|
2021-03-05 12:20:51 +00:00
|
|
|
|
u,
|
|
|
|
|
openGraph))
|
|
|
|
|
}
|
2020-08-05 15:08:59 +00:00
|
|
|
|
}
|