diff --git a/editor.go b/editor.go index 0f46af3..3d70aa4 100644 --- a/editor.go +++ b/editor.go @@ -143,7 +143,29 @@ func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) { return } http.Redirect(w, r, post.Path, http.StatusFound) - return + case "helpgpx": + file, _, err := r.FormFile("file") + if err != nil { + a.serveError(w, r, err.Error(), http.StatusBadRequest) + return + } + gpx, err := io.ReadAll(file) + if err != nil { + a.serveError(w, r, err.Error(), http.StatusBadRequest) + return + } + var gpxBuffer bytes.Buffer + _, _ = a.min.Write(&gpxBuffer, contenttype.XML, gpx) + resultMap := map[string]string{ + "gpx": gpxBuffer.String(), + } + resultBytes, err := yaml.Marshal(resultMap) + if err != nil { + a.serveError(w, r, err.Error(), http.StatusBadRequest) + return + } + w.Header().Set(contentType, contenttype.TextUTF8) + _, _ = w.Write(resultBytes) default: a.serveError(w, r, "Unknown editoraction", http.StatusBadRequest) } diff --git a/geo.go b/geo.go index f131078..9542521 100644 --- a/geo.go +++ b/geo.go @@ -1,9 +1,12 @@ package main import ( + "embed" "fmt" "io" "net/http" + "net/http/httptest" + "net/http/httputil" "net/url" "strings" @@ -70,3 +73,46 @@ func (a *goBlog) photonReverse(lat, lon float64, lang string) ([]byte, error) { func geoOSMLink(g *gogeouri.Geo) string { return fmt.Sprintf("https://www.openstreetmap.org/?mlat=%v&mlon=%v", g.Latitude, g.Longitude) } + +//go:embed leaflet/* +var leafletFiles embed.FS + +func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc { + osmUrl, _ := url.Parse("https://tile.openstreetmap.org/") + tileProxy := http.StripPrefix(basePath, httputil.NewSingleHostReverseProxy(osmUrl)) + return func(w http.ResponseWriter, r *http.Request) { + proxyTarget := "https://tile.openstreetmap.org" + r.URL.Path + proxyRequest, _ := http.NewRequest(http.MethodGet, proxyTarget, nil) + // Copy request headers + for _, k := range []string{ + "Accept-Encoding", + "Accept-Language", + "Accept", + "Cache-Control", + "If-Modified-Since", + "If-None-Match", + "User-Agent", + } { + proxyRequest.Header.Set(k, r.Header.Get(k)) + } + rec := httptest.NewRecorder() + tileProxy.ServeHTTP(rec, proxyRequest) + res := rec.Result() + // Copy result headers + for _, k := range []string{ + "Accept-Ranges", + "Access-Control-Allow-Origin", + "Age", + "Cache-Control", + "Content-Length", + "Content-Type", + "Etag", + "Expires", + } { + w.Header().Set(k, res.Header.Get(k)) + } + w.WriteHeader(res.StatusCode) + _, _ = io.Copy(w, res.Body) + _ = res.Body.Close() + } +} diff --git a/geoMap.go b/geoMap.go index 31654c7..cb121d9 100644 --- a/geoMap.go +++ b/geoMap.go @@ -1,18 +1,10 @@ package main import ( - "embed" "encoding/json" - "io" "net/http" - "net/http/httptest" - "net/http/httputil" - "net/url" ) -//go:embed leaflet/* -var leafletFiles embed.FS - const defaultGeoMapPath = "/map" func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) { @@ -68,48 +60,7 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) { BlogString: blog, Canonical: a.getFullAddress(mapPath), Data: map[string]interface{}{ - "mappath": mapPath, "locations": string(jb), }, }) } - -func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc { - osmUrl, _ := url.Parse("https://tile.openstreetmap.org/") - tileProxy := http.StripPrefix(basePath, httputil.NewSingleHostReverseProxy(osmUrl)) - return func(w http.ResponseWriter, r *http.Request) { - proxyTarget := "https://tile.openstreetmap.org" + r.URL.Path - proxyRequest, _ := http.NewRequest(http.MethodGet, proxyTarget, nil) - // Copy request headers - for _, k := range []string{ - "Accept-Encoding", - "Accept-Language", - "Accept", - "Cache-Control", - "If-Modified-Since", - "If-None-Match", - "User-Agent", - } { - proxyRequest.Header.Set(k, r.Header.Get(k)) - } - rec := httptest.NewRecorder() - tileProxy.ServeHTTP(rec, proxyRequest) - res := rec.Result() - // Copy result headers - for _, k := range []string{ - "Accept-Ranges", - "Access-Control-Allow-Origin", - "Age", - "Cache-Control", - "Content-Length", - "Content-Type", - "Etag", - "Expires", - } { - w.Header().Set(k, res.Header.Get(k)) - } - w.WriteHeader(res.StatusCode) - _, _ = io.Copy(w, res.Body) - _ = res.Body.Close() - } -} diff --git a/geoTrips.go b/geoTrips.go new file mode 100644 index 0000000..cba02a0 --- /dev/null +++ b/geoTrips.go @@ -0,0 +1,118 @@ +package main + +import ( + "encoding/json" + "errors" + "log" + "math" + + "github.com/tkrajina/gpxgo/gpx" + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +func (p *post) HasTrip() bool { + return p.firstParameter("gpx") != "" +} + +type tripRenderResult struct { + HasPoints bool + Paths [][]*tripPoint + PathsJSON string + Kilometers string + Hours string + Name string +} + +func (a *goBlog) renderTrip(p *post) (result *tripRenderResult, err error) { + gpxString := p.firstParameter("gpx") + if gpxString == "" { + return nil, errors.New("no gpx parameter in post") + } + + // Parse GPX + parseResult, err := tripParseGPX(gpxString) + if err != nil { + return nil, err + } + + l, _ := language.Parse(a.cfg.Blogs[p.Blog].Lang) + lp := message.NewPrinter(l) + + pathsJSON, err := json.Marshal(parseResult.paths) + if err != nil { + return nil, err + } + + result = &tripRenderResult{ + HasPoints: len(parseResult.paths) > 0 && len(parseResult.paths[0]) > 0, + Paths: parseResult.paths, + PathsJSON: string(pathsJSON), + Name: parseResult.gpxData.Name, + Kilometers: lp.Sprintf("%.2f", parseResult.md.MovingDistance/1000), + Hours: lp.Sprintf( + "%.0f:%2.0f:%2.0f", + math.Floor(parseResult.md.MovingTime/3600), // Hours + math.Floor(math.Mod(parseResult.md.MovingTime, 3600)/60), // Minutes + math.Floor(math.Mod(parseResult.md.MovingTime, 60)), // Seconds + ), + } + + log.Println(string(pathsJSON)) + + return result, nil +} + +type tripPoint struct { + Lat, Lon float64 +} + +type tripParseResult struct { + paths [][]*tripPoint + gpxData *gpx.GPX + md *gpx.MovingData +} + +func tripParseGPX(gpxString string) (result *tripParseResult, err error) { + result = &tripParseResult{} + + type tripPath struct { + gpxMovingData *gpx.MovingData + points []*tripPoint + } + + var paths []*tripPath + + result.gpxData, err = gpx.ParseString(gpxString) + if err != nil { + return nil, err + } + for _, track := range result.gpxData.Tracks { + for _, segment := range track.Segments { + md := segment.MovingData() + path := &tripPath{ + gpxMovingData: &md, + } + for _, point := range segment.Points { + path.points = append(path.points, &tripPoint{ + Lat: point.GetLatitude(), Lon: point.GetLongitude(), + }) + } + paths = append(paths, path) + } + } + + result.md = &gpx.MovingData{} + for _, path := range paths { + // Combine moving data + result.md.MaxSpeed = math.Max(result.md.MaxSpeed, path.gpxMovingData.MaxSpeed) + result.md.MovingDistance = result.md.MovingDistance + path.gpxMovingData.MovingDistance + result.md.MovingTime = result.md.MovingTime + path.gpxMovingData.MovingTime + result.md.StoppedDistance = result.md.StoppedDistance + path.gpxMovingData.StoppedDistance + result.md.StoppedTime = result.md.StoppedTime + path.gpxMovingData.StoppedTime + // Add points + result.paths = append(result.paths, path.points) + } + + return result, nil +} diff --git a/geoTrips_test.go b/geoTrips_test.go new file mode 100644 index 0000000..b03fd1c --- /dev/null +++ b/geoTrips_test.go @@ -0,0 +1,59 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_renderTrip(t *testing.T) { + app := &goBlog{ + cfg: &config{ + Db: &configDb{ + File: filepath.Join(t.TempDir(), "test.db"), + }, + Server: &configServer{}, + Blogs: map[string]*configBlog{ + "en": { + Lang: "en", + }, + "de": { + Lang: "de", + }, + }, + }, + } + + _ = app.initDatabase(false) + app.initComponents(false) + + gpxBytes, _ := os.ReadFile("testdata/test.gpx") + + post := &post{ + Blog: "en", + Parameters: map[string][]string{ + "gpx": { + string(gpxBytes), + }, + }, + } + + resEn, err := app.renderTrip(post) + require.NoError(t, err) + + assert.True(t, resEn.HasPoints) + assert.Equal(t, "2.70", resEn.Kilometers) + assert.Equal(t, "0:42:53", resEn.Hours) + + post.Blog = "de" + + resDe, err := app.renderTrip(post) + require.NoError(t, err) + + assert.True(t, resDe.HasPoints) + assert.Equal(t, "2,70", resDe.Kilometers) + assert.Equal(t, "0:42:53", resDe.Hours) +} diff --git a/go.mod b/go.mod index bdfc3a2..f6dd188 100644 --- a/go.mod +++ b/go.mod @@ -41,14 +41,16 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tdewolff/minify/v2 v2.9.22 github.com/thoas/go-funk v0.9.1 + github.com/tkrajina/gpxgo v1.1.2 github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/yuin/goldmark v1.4.3 // master github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa - golang.org/x/net v0.0.0-20211108170745-6635138e15ea + golang.org/x/net v0.0.0-20211109214657-ef0fda0de508 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/text v0.3.7 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b tailscale.com v1.16.2 // main @@ -102,8 +104,7 @@ require ( go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect - golang.org/x/sys v0.0.0-20211109065445-02f5c0300f6e // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c // indirect golang.zx2c4.com/wireguard/windows v0.4.10 // indirect diff --git a/go.sum b/go.sum index 3cc5aee..17e6ea2 100644 --- a/go.sum +++ b/go.sum @@ -280,6 +280,7 @@ github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df h1:nsRFf9ZkcalB12ZJZY github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4 h1:d2oKwfgLl3ef0PyYDkzjsVyYlBZzNpOpXitDraOnVXc= github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4/go.mod h1:vt0iOV52/wq97Ql/jp7mUkqyrlEiGQuhHic4bVoHy0c= +github.com/joeshaw/gengen v0.0.0-20190604015154-c77d87825f5a/go.mod h1:v2qvRL8Xwk4OlARK6gPlf2JreZXzv0dYp/8+kUJ0y7Q= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= @@ -452,6 +453,8 @@ github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/tkrajina/gpxgo v1.1.2 h1:il6rjS6IGm3yqa/yr7+fKBlF3ufWDEPZrYi/kxI1Jv0= +github.com/tkrajina/gpxgo v1.1.2/go.mod h1:795sjVRFo5wWyN6oOZp0RYienGGBJjpAlgOz2nCngA0= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= @@ -587,8 +590,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211108170745-6635138e15ea h1:FosBMXtOc8Tp9Hbo4ltl1WJSrTVewZU8MPnTPY2HdH8= -golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211109214657-ef0fda0de508 h1:v3NKo+t/Kc3EASxaKZ82lwK6mCf4ZeObQBduYFZHo7c= +golang.org/x/net v0.0.0-20211109214657-ef0fda0de508/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -687,8 +690,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211109065445-02f5c0300f6e h1:i6Vklmyu+fZMFYpum+sR4ZWABGW7MyIxfJZXYvcnbns= -golang.org/x/sys v0.0.0-20211109065445-02f5c0300f6e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3 h1:T6tyxxvHMj2L1R2kZg0uNMpS8ZhB9lRa9XRGTCSA65w= +golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= @@ -717,6 +720,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190603231351-8aaa1484dc10/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= diff --git a/http.go b/http.go index 708094a..be1c70a 100644 --- a/http.go +++ b/http.go @@ -187,6 +187,9 @@ func (a *goBlog) buildRouter() (http.Handler, error) { // Media files r.Route("/m", a.mediaFilesRouter) + // Other routes + r.Route("/x", a.xRouter) + // Captcha r.Handle("/captcha/*", captcha.Server(500, 250)) diff --git a/httpMiddlewares.go b/httpMiddlewares.go index a58c479..585d59e 100644 --- a/httpMiddlewares.go +++ b/httpMiddlewares.go @@ -41,7 +41,7 @@ func (a *goBlog) securityHeaders(next http.Handler) http.Handler { w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "SAMEORIGIN") w.Header().Set("X-Xss-Protection", "1; mode=block") - w.Header().Set("Content-Security-Policy", "default-src 'self'"+cspDomains) + w.Header().Set("Content-Security-Policy", "default-src 'self'"+cspDomains+"; img-src 'self' "+cspDomains+" data:") next.ServeHTTP(w, r) }) } diff --git a/httpRouters.go b/httpRouters.go index 25af67f..46b51f2 100644 --- a/httpRouters.go +++ b/httpRouters.go @@ -96,6 +96,13 @@ func (a *goBlog) mediaFilesRouter(r chi.Router) { r.Get(mediaFileRoute, a.serveMediaFile) } +// Various other routes +func (a *goBlog) xRouter(r chi.Router) { + r.Use(a.privateModeHandler) + r.Get("/tiles/{z}/{x}/{y}.png", a.proxyTiles("/x/tiles")) + r.With(cacheLoggedIn, a.cacheMiddleware).HandleFunc("/leaflet/*", a.serveFs(leafletFiles, "/x/")) +} + // Blog func (a *goBlog) blogRouter(blog string, conf *configBlog) func(r chi.Router) { return func(r chi.Router) { @@ -398,14 +405,7 @@ func (a *goBlog) blogGeoMapRouter(conf *configBlog) func(r chi.Router) { return func(r chi.Router) { if mc := conf.Map; mc != nil && mc.Enabled { mapPath := conf.getRelativePath(defaultIfEmpty(mc.Path, defaultGeoMapPath)) - r.Route(mapPath, func(r chi.Router) { - r.Use(a.privateModeHandler) - r.Group(func(r chi.Router) { - r.With(a.cacheMiddleware).Get("/", a.serveGeoMap) - r.With(cacheLoggedIn, a.cacheMiddleware).HandleFunc("/leaflet/*", a.serveFs(leafletFiles, mapPath+"/")) - }) - r.Get("/tiles/{z}/{x}/{y}.png", a.proxyTiles(mapPath+"/tiles")) - }) + r.With(a.privateModeHandler, a.cacheMiddleware).Get(mapPath, a.serveGeoMap) } } } diff --git a/pkgs/contenttype/contenttype.go b/pkgs/contenttype/contenttype.go index c7a93de..a688dcf 100644 --- a/pkgs/contenttype/contenttype.go +++ b/pkgs/contenttype/contenttype.go @@ -15,13 +15,15 @@ const ( LDJSON = "application/ld+json" MultipartForm = "multipart/form-data" RSS = "application/rss+xml" + Text = "text/plain" WWWForm = "application/x-www-form-urlencoded" XML = "text/xml" ASUTF8 = AS + CharsetUtf8Suffix CSSUTF8 = CSS + CharsetUtf8Suffix HTMLUTF8 = HTML + CharsetUtf8Suffix - JSUTF8 = JS + CharsetUtf8Suffix JSONUTF8 = JSON + CharsetUtf8Suffix + JSUTF8 = JS + CharsetUtf8Suffix + TextUTF8 = Text + CharsetUtf8Suffix XMLUTF8 = XML + CharsetUtf8Suffix ) diff --git a/render.go b/render.go index ab6ed93..65c8268 100644 --- a/render.go +++ b/render.go @@ -61,6 +61,7 @@ func (a *goBlog) initRendering() error { "likelink": a.likeLink, "liketitle": a.likeTitle, "photolinks": a.photoLinks, + "rendertrip": a.renderTrip, // Others "dateformat": dateFormat, "isodate": isoDateFormat, diff --git a/templates/assets/js/geomap.js b/templates/assets/js/geomap.js index 86d67c0..69b3184 100644 --- a/templates/assets/js/geomap.js +++ b/templates/assets/js/geomap.js @@ -4,7 +4,7 @@ let map = L.map('map') - L.tileLayer(mapEl.dataset.tiles, { + L.tileLayer("/x/tiles/{z}/{x}/{y}.png", { attribution: '© OpenStreetMap contributors' }).addTo(map) @@ -16,7 +16,5 @@ }) markers.push(marker) }) - - map.fitBounds(markers) - map.zoomOut(2, { animate: false }) + map.fitBounds(markers, { padding: [5, 5] }) })() \ No newline at end of file diff --git a/templates/assets/js/geotrip.js b/templates/assets/js/geotrip.js new file mode 100644 index 0000000..4f5363c --- /dev/null +++ b/templates/assets/js/geotrip.js @@ -0,0 +1,22 @@ +(function () { + let mapEl = document.getElementById('map') + let paths = JSON.parse(mapEl.dataset.paths) + + let map = L.map('map') + + L.tileLayer("/x/tiles/{z}/{x}/{y}.png", { + attribution: '© OpenStreetMap contributors' + }).addTo(map) + + let polylines = [] + paths.forEach(path => { + let pathPoints = [] + path.forEach(point => { + pathPoints.push([point.Lat, point.Lon]) + }) + let pl = L.polyline(pathPoints, { color: 'blue' }).addTo(map) + polylines.push(pl) + }) + let fgb = L.featureGroup(polylines).getBounds() + map.fitBounds(fgb, { padding: [5, 5] }) +})() \ No newline at end of file diff --git a/templates/editor.gohtml b/templates/editor.gohtml index a639db2..4bbd88a 100644 --- a/templates/editor.gohtml +++ b/templates/editor.gohtml @@ -44,6 +44,14 @@ +

{{ string .Blog.Lang "gpxhelper" }}

+

{{ string .Blog.Lang "gpxhelperdesc" }}

+
+ + + +
+ diff --git a/templates/geomap.gohtml b/templates/geomap.gohtml index a22eb3a..b9176bf 100644 --- a/templates/geomap.gohtml +++ b/templates/geomap.gohtml @@ -1,8 +1,8 @@ {{ define "title" }} {{ mdtitle .Blog.Title }} {{ if not .Data.nolocations }} - - + + {{ end }} {{ end }} @@ -11,7 +11,7 @@ {{ if .Data.nolocations }}

{{ string .Blog.Lang "nolocations" }}

{{ else }} -
+
{{ end }} diff --git a/templates/post.gohtml b/templates/post.gohtml index f7950d5..d54e4db 100644 --- a/templates/post.gohtml +++ b/templates/post.gohtml @@ -2,6 +2,7 @@ {{ with .Data.RenderedTitle }}{{ . }} - {{end}}{{ mdtitle .Blog.Title }} {{ include "postheadmeta" . }} {{ with shorturl .Data }}{{ end }} + {{ include "tripheader" . }} {{ end }} {{ define "main" }} @@ -17,6 +18,7 @@ {{ content .Data false }} {{ end }} + {{ include "tripdetails" . }} {{ include "posttax" . }} {{ include "author" . }} diff --git a/templates/strings/de.yaml b/templates/strings/de.yaml index 8179192..8c0e872 100644 --- a/templates/strings/de.yaml +++ b/templates/strings/de.yaml @@ -18,8 +18,11 @@ editorpostdesc: "Leere Parameter werden automatisch entfernt. Mehr mögliche Par emailopt: "E-Mail (optional)" fileuses: "Datei-Verwendungen" gentts: "Text-To-Speech-Audio erzeugen" +gpxhelper: "GPX-Helfer" +gpxhelperdesc: "GPX minimieren und YAML für das Frontmatter generieren." interactions: "Interaktionen & Kommentare" interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein." +kilometers: "Kilometer" likeof: "Gefällt mir von" loading: "Laden..." location: "Standort" diff --git a/templates/strings/default.yaml b/templates/strings/default.yaml index 1c42e7a..7a23295 100644 --- a/templates/strings/default.yaml +++ b/templates/strings/default.yaml @@ -22,9 +22,12 @@ emailopt: "Email (optional)" feed: "Feed" fileuses: "file uses" gentts: "Generate Text-To-Speech audio" +gpxhelper: "GPX helper" +gpxhelperdesc: "Minify GPX and generate YAML for the frontmatter." indieauth: "IndieAuth" interactions: "Interactions & Comments" interactionslabel: "Have you published a response to this? Paste the URL here." +kilometers: "kilometers" likeof: "Like of" loading: "Loading..." location: "Location" diff --git a/templates/tripdetails.gohtml b/templates/tripdetails.gohtml new file mode 100644 index 0000000..8df9977 --- /dev/null +++ b/templates/tripdetails.gohtml @@ -0,0 +1,11 @@ +{{ define "tripdetails" }} + {{ if .Data.HasTrip }} + {{ $trip := (rendertrip .Data) }} + {{ if $trip.HasPoints }} + {{ $lang := .Blog.Lang }} +

{{ with $trip.Name }}{{ . }} {{ end }}{{ with $trip.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $trip.Hours }}⌛ {{ . }}{{ end }}

+
+ + {{ end }} + {{ end }} +{{ end }} \ No newline at end of file diff --git a/templates/tripheader.gohtml b/templates/tripheader.gohtml new file mode 100644 index 0000000..da767ef --- /dev/null +++ b/templates/tripheader.gohtml @@ -0,0 +1,6 @@ +{{ define "tripheader" }} + {{ if .Data.HasTrip }} + + + {{ end }} +{{ end }} \ No newline at end of file diff --git a/testdata/test.gpx b/testdata/test.gpx new file mode 100644 index 0000000..9c270e3 --- /dev/null +++ b/testdata/test.gpx @@ -0,0 +1,1049 @@ + + + + Nach Dybbøl Mølle (Dybboel Moelle) + + + komoot + text/html + + + + + Nach Dybbøl Mølle (Dybboel Moelle) + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.414814 + + + + 66.366348 + + + + 66.286475 + + + + 66.209298 + + + + 66.130842 + + + + 66.016704 + + + + 65.941305 + + + + 65.863624 + + + + 65.787943 + + + + 65.711947 + + + + 65.548873 + + + + 65.475775 + + + + 65.396942 + + + + 65.316794 + + + + 65.239831 + + + + 65.157279 + + + + 65.077529 + + + + 64.997934 + + + + 64.835005 + + + + 64.527657 + + + + 64.239479 + + + + 63.932381 + + + + 63.627066 + + + + 63.345822 + + + + 63.045085 + + + + 62.753718 + + + + 62.475130 + + + + 62.189144 + + + + 61.900439 + + + + 61.616049 + + + + 61.319335 + + + + 61.024682 + + + + 60.715059 + + + + 60.424946 + + + + 60.129907 + + + + 59.845516 + + + + 59.539043 + + + + 59.065301 + + + + 58.420375 + + + + 57.747535 + + + + 57.092342 + + + + 56.486032 + + + + 55.868564 + + + + 55.224614 + + + + 54.528548 + + + + 53.874838 + + + + 53.239380 + + + + 52.587484 + + + + 51.974894 + + + + 51.333958 + + + + 50.681909 + + + + 50.007710 + + + + 49.344960 + + + + 48.709492 + + + + 48.054659 + + + + 47.387798 + + + + 47.424468 + + + + 47.618380 + + + + 47.815589 + + + + 48.000759 + + + + 48.196279 + + + + 48.387262 + + + + 48.579702 + + + + 48.770024 + + + + 48.964494 + + + + 49.155708 + + + + 49.355647 + + + + 49.565456 + + + + 49.758405 + + + + 50.037334 + + + + 50.223381 + + + + 50.412730 + + + + 50.611810 + + + + 50.833402 + + + + 50.870338 + + + + 50.745068 + + + + 50.613614 + + + + 50.483091 + + + + 50.348394 + + + + 50.220944 + + + + 50.084786 + + + + 49.945301 + + + + 49.807695 + + + + 49.675659 + + + + 49.545741 + + + + 49.416001 + + + + 49.284820 + + + + 49.155790 + + + + 49.007262 + + + + 48.870602 + + + + 48.744174 + + + + 48.616717 + + + + 48.488378 + + + + 48.945741 + + + + 49.490295 + + + + 50.067648 + + + + 50.580503 + + + + 51.134194 + + + + 51.702540 + + + + 52.369380 + + + + 52.904725 + + + + 53.523282 + + + + 54.053167 + + + + 54.600104 + + + + 55.124856 + + + + 55.664052 + + + + 56.219155 + + + + 56.757335 + + + + 57.284213 + + + + 57.827283 + + + + 58.374717 + + + + 58.873936 + + + + 59.305991 + + + + 59.726908 + + + + 60.141701 + + + + 60.577239 + + + + 61.002768 + + + + 61.412453 + + + + 61.849195 + + + + 62.272277 + + + + 62.714238 + + + + 63.166079 + + + + 63.581314 + + + + 64.029081 + + + + 64.441395 + + + + 64.874391 + + + + 65.298993 + + + + 65.704618 + + + + 66.121207 + + + + 66.537221 + + + + 66.695406 + + + + 66.699756 + + + + 66.703883 + + + + 66.708264 + + + + 66.712484 + + + + 66.716779 + + + + 66.721033 + + + + 66.725238 + + + + 66.729572 + + + + 66.733793 + + + + 66.738082 + + + + 66.742359 + + + + 66.746333 + + + + 66.750495 + + + + 66.754839 + + + + 66.758843 + + + + 66.762873 + + + + 66.767242 + + + + 66.771401 + + + + 66.900165 + + + + 67.082627 + + + + 67.266497 + + + + 67.439654 + + + + 67.628941 + + + + 67.808393 + + + + 67.994002 + + + + 68.179209 + + + + 68.357646 + + + + 68.538113 + + + + 68.728460 + + + + 68.920872 + + + + 69.102911 + + + + 69.301723 + + + + 69.488964 + + + + 69.659056 + + + + 69.829001 + + + + 70.005741 + + + + 70.197189 + + + + 70.377249 + + + + 70.548129 + + + + 70.970845 + + + + 71.143296 + + + + 71.329031 + + + + 71.511837 + + + + 71.681894 + + + + 71.862671 + + + + 72.044273 + + + + 72.223014 + + + + 72.396733 + + + + 72.563994 + + + + 72.737713 + + + + 72.907220 + + + + 73.224178 + + + + 73.403974 + + + + 73.632263 + + + + 73.972316 + + + + 74.281814 + + + + 74.613441 + + + + 74.951140 + + + + 75.285258 + + + + 75.640430 + + + + 75.972824 + + + + 76.306289 + + + + 76.636723 + + + + 76.971055 + + + + 77.283057 + + + + 77.628868 + + + + 77.968609 + + + + 78.293358 + + + + 78.751459 + + + + 79.066855 + + + + 79.406016 + + + + 79.771048 + + + + 80.303269 + + + + 80.839755 + + + + 81.366847 + + + + 81.940721 + + + + 82.457670 + + + + 83.033742 + + + + 83.604871 + + + + 84.167937 + + + + 84.749014 + + + + 85.262844 + + + + 85.796544 + + + + 86.355313 + + + + 86.883230 + + + + 87.415467 + + + + 87.948830 + + + + 88.486100 + + + + 89.039630 + + + + 89.615090 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + 89.961305 + + + + + \ No newline at end of file diff --git a/utils.go b/utils.go index 06276ce..83ae54f 100644 --- a/utils.go +++ b/utils.go @@ -220,8 +220,8 @@ func urlHasExt(rawUrl string, allowed ...string) (ext string, has bool) { return ext, funk.ContainsString(allowed, strings.ToLower(ext)) } -// Get SHA-256 hash of file -func getSHA256(file io.ReadSeeker) (filename string, err error) { +// Get SHA-256 hash +func getSHA256(file io.ReadSeeker) (hash string, err error) { if _, err = file.Seek(0, 0); err != nil { return "", err }