added static and framed external rendering (untested)
This commit is contained in:
+3
-1
@@ -63,6 +63,7 @@ type AmConfig struct {
|
||||
Title string `yaml:"title"`
|
||||
Text string `yaml:"text"`
|
||||
} `yaml:"userAgreement"`
|
||||
ExternalPath string `yaml:"externalPath"`
|
||||
} `yaml:"site"`
|
||||
Database struct {
|
||||
Driver string `yaml:"driver"`
|
||||
@@ -212,7 +213,7 @@ func overlayOptionFlag(loaded, defaulted bool) bool {
|
||||
|
||||
/* overlayConfig takes two configuration structures and overlays them to create the third.
|
||||
* Parameters:
|
||||
* dest - Points to the destination copnfiguration structure.
|
||||
* dest - Points to the destination configuration structure.
|
||||
* loaded - Points to the loaded configuration structure.
|
||||
* defaults - Points to the default configuration structure.
|
||||
*/
|
||||
@@ -225,6 +226,7 @@ func overlayConfig(dest *AmConfig, loaded *AmConfig, defaults *AmConfig) {
|
||||
dest.Site.SessionExpire = overlayString(loaded.Site.SessionExpire, defaults.Site.SessionExpire)
|
||||
dest.Site.UserAgreement.Title = overlayString(loaded.Site.UserAgreement.Title, defaults.Site.UserAgreement.Title)
|
||||
dest.Site.UserAgreement.Text = overlayString(loaded.Site.UserAgreement.Text, defaults.Site.UserAgreement.Text)
|
||||
dest.Site.ExternalPath = overlayString(loaded.Site.ExternalPath, defaults.Site.ExternalPath)
|
||||
dest.Database.Driver = overlayString(loaded.Database.Driver, defaults.Database.Driver)
|
||||
dest.Database.Dsn = overlayString(loaded.Database.Dsn, defaults.Database.Dsn)
|
||||
dest.Defaults.Language = overlayString(loaded.Defaults.Language, defaults.Defaults.Language)
|
||||
|
||||
@@ -17,6 +17,7 @@ site:
|
||||
title: "Amsterdam User Agreement"
|
||||
text: >
|
||||
Text of this agreement is TBD.
|
||||
externalPath: ""
|
||||
database:
|
||||
driver: "mysql"
|
||||
dsn: "amsdb:x00yes2k@tcp(localhost)/amsterdam?parseTime=true&loc=UTC"
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/labstack/gommon v0.4.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/tkuchiki/go-timezone v0.2.3
|
||||
golang.org/x/text v0.30.0
|
||||
golang.org/x/text v0.34.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -29,9 +29,9 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -57,17 +57,25 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -52,6 +52,15 @@ func setupEcho() *echo.Echo {
|
||||
uiset := []echo.MiddlewareFunc{ui.SessionStoreInjector, ui.ContextCreator, ui.IPBanTest, ui.CookieLoginTest}
|
||||
|
||||
e.RouteNotFound("/*", ui.AmWrap(AmNotFoundHandler), uiset...)
|
||||
if config.GlobalConfig.Site.ExternalPath != "" {
|
||||
root, err := os.OpenRoot(config.GlobalConfig.Site.ExternalPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fs := root.FS()
|
||||
e.StaticFS("/ext", fs)
|
||||
e.GET("/fext/*", ui.AmWrap(ui.AmStaticFramePage(fs, "/fext/")), uiset...)
|
||||
}
|
||||
e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...)
|
||||
e.GET("/img/*", ui.AmServeImage)
|
||||
if config.GlobalConfig.Rendering.VeniceCompatibleImageURLs {
|
||||
|
||||
@@ -11,11 +11,15 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
//go:embed static/*
|
||||
@@ -29,3 +33,92 @@ func AmStaticFileHandler() echo.HandlerFunc {
|
||||
}
|
||||
return echo.WrapHandler(http.StripPrefix("/static/", http.FileServer(http.FS(fsys))))
|
||||
}
|
||||
|
||||
// extractPlainText extracts all plain text from a HTML tree node.
|
||||
func extractPlainText(n *html.Node) string {
|
||||
var sb strings.Builder
|
||||
var walk func(*html.Node)
|
||||
walk = func(n *html.Node) {
|
||||
if n.Type == html.TextNode {
|
||||
sb.WriteString(n.Data)
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
walk(c)
|
||||
}
|
||||
}
|
||||
walk(n)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// extractInnerHTML extracts the inner HTML from a HTML tree node.
|
||||
func extractInnerHTML(n *html.Node) string {
|
||||
var buf bytes.Buffer
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
html.Render(&buf, c)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// breakUpHTML extracts the title and body from an HTML page.
|
||||
func breakUpHTML(r io.Reader) (string, string, error) {
|
||||
doc, err := html.Parse(r)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
title := ""
|
||||
body := ""
|
||||
var traverse func(*html.Node)
|
||||
traverse = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
switch n.Data {
|
||||
case "title":
|
||||
body = extractPlainText(n)
|
||||
case "body":
|
||||
body = extractInnerHTML(n)
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
traverse(c)
|
||||
}
|
||||
}
|
||||
traverse(doc)
|
||||
return title, body, nil
|
||||
}
|
||||
|
||||
/* AmStaticFramePage generates a handler that will serve up data from an external filesystem "framed" inside
|
||||
* the frame with the template engine.
|
||||
* Parameters:
|
||||
* staticFS - The filesystem to serve from.
|
||||
* prefix - The prefix to be stripped off pathnames to feed to the filesystem.
|
||||
* Returns:
|
||||
* An AmPageFunc suitable for wrapping and adding to the Echo handlers.
|
||||
*/
|
||||
func AmStaticFramePage(staticFS fs.FS, prefix string) AmPageFunc {
|
||||
return func(ctxt AmContext) (string, any) {
|
||||
fname := ctxt.URLPath()
|
||||
if strings.HasPrefix(fname, prefix) {
|
||||
fname = fname[len(prefix):]
|
||||
} else {
|
||||
return "error", "invalid path name"
|
||||
}
|
||||
mtype := mimeTypeFromFilename(fname)
|
||||
ctxt.VarMap().Set("mimeType", mtype)
|
||||
switch mtype {
|
||||
case "text/html":
|
||||
f, err := staticFS.Open(fname)
|
||||
if err != nil {
|
||||
return "error", err
|
||||
}
|
||||
defer f.Close()
|
||||
title, body, err := breakUpHTML(f)
|
||||
if err != nil {
|
||||
return "error", err
|
||||
}
|
||||
ctxt.SetFrameTitle(title)
|
||||
ctxt.VarMap().Set("title", title)
|
||||
ctxt.VarMap().Set("data", body)
|
||||
return "framed", "extern.jet"
|
||||
}
|
||||
return "error", "Unknown MIME Type: " + mtype
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{*
|
||||
* Amsterdam Web Communities System
|
||||
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*}
|
||||
<div class="p-4">
|
||||
<!-- Page Title -->
|
||||
<div class="mb-6">
|
||||
<h1 class="text-blue-800 text-4xl font-bold mb-2">{{ title | raw }}</h1>
|
||||
<hr class="border-2 border-gray-400 w-4/5 mb-6">
|
||||
</div>
|
||||
|
||||
{{ if mimeType == "text/html" }}
|
||||
{{ data | raw }}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user