mirror of https://github.com/jlelse/GoBlog
Editor live preview
This commit is contained in:
parent
53e90075f5
commit
bbfc68d145
47
editor.go
47
editor.go
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/microcosm-cc/bluemonday"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
@ -24,6 +26,51 @@ func (a *goBlog) serveEditor(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
EnableCompression: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
for {
|
||||||
|
// Retrieve content
|
||||||
|
mt, message, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Create preview
|
||||||
|
preview, err := a.createMarkdownPreview(message)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Write preview to socket
|
||||||
|
err = c.WriteMessage(mt, preview)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) createMarkdownPreview(markdown []byte) (rendered []byte, err error) {
|
||||||
|
mdString := string(markdown)
|
||||||
|
if split := strings.Split(mdString, "---\n"); len(split) >= 3 && len(strings.TrimSpace(split[0])) == 0 {
|
||||||
|
// Remove frontmatter from content
|
||||||
|
mdString = strings.Join(split[2:], "---\n")
|
||||||
|
}
|
||||||
|
// Render markdown
|
||||||
|
rendered, err = a.renderMarkdown(mdString, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Sanitize HTML
|
||||||
|
rendered = bluemonday.UGCPolicy().SanitizeBytes(rendered)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveEditorPost(w http.ResponseWriter, r *http.Request) {
|
||||||
blog := r.Context().Value(blogKey).(string)
|
blog := r.Context().Value(blogKey).(string)
|
||||||
if action := r.FormValue("editoraction"); action != "" {
|
if action := r.FormValue("editoraction"); action != "" {
|
||||||
|
|
32
geoMap.go
32
geoMap.go
|
@ -8,12 +8,11 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed leaflet/*
|
||||||
|
var leafletFiles embed.FS
|
||||||
|
|
||||||
const defaultGeoMapPath = "/map"
|
const defaultGeoMapPath = "/map"
|
||||||
|
|
||||||
func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -75,31 +74,6 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed leaflet/*
|
|
||||||
var leafletFiles embed.FS
|
|
||||||
|
|
||||||
func (a *goBlog) serveLeaflet(basePath string) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fileName := strings.TrimPrefix(r.URL.Path, basePath)
|
|
||||||
fb, err := leafletFiles.ReadFile(fileName)
|
|
||||||
if err != nil {
|
|
||||||
a.serve404(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch path.Ext(fileName) {
|
|
||||||
case ".js":
|
|
||||||
w.Header().Set(contentType, contenttype.JS)
|
|
||||||
_, _ = a.min.Write(w, contenttype.JSUTF8, fb)
|
|
||||||
case ".css":
|
|
||||||
w.Header().Set(contentType, contenttype.CSS)
|
|
||||||
_, _ = a.min.Write(w, contenttype.CSSUTF8, fb)
|
|
||||||
default:
|
|
||||||
w.Header().Set(contentType, http.DetectContentType(fb))
|
|
||||||
_, _ = w.Write(fb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc {
|
func (a *goBlog) proxyTiles(basePath string) http.HandlerFunc {
|
||||||
osmUrl, _ := url.Parse("https://tile.openstreetmap.org/")
|
osmUrl, _ := url.Parse("https://tile.openstreetmap.org/")
|
||||||
tileProxy := http.StripPrefix(basePath, httputil.NewSingleHostReverseProxy(osmUrl))
|
tileProxy := http.StripPrefix(basePath, httputil.NewSingleHostReverseProxy(osmUrl))
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -20,6 +20,7 @@ require (
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/securecookie v1.1.1
|
github.com/gorilla/securecookie v1.1.1
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df
|
github.com/jlaffaye/ftp v0.0.0-20211029032751-b1140299f4df
|
||||||
// master
|
// master
|
||||||
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
||||||
|
@ -47,7 +48,6 @@ require (
|
||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
// main
|
|
||||||
tailscale.com v1.16.2
|
tailscale.com v1.16.2
|
||||||
willnorris.com/go/microformats v1.1.1
|
willnorris.com/go/microformats v1.1.1
|
||||||
)
|
)
|
||||||
|
@ -100,7 +100,7 @@ require (
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||||
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 // indirect
|
golang.org/x/sys v0.0.0-20211031064116-611d5d643895 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // 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 v0.0.0-20210905140043-2ef39d47540c // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -240,6 +240,8 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||||
|
@ -683,8 +685,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-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-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-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 h1:giLT+HuUP/gXYrG2Plg9WTjj4qhfgaW424ZIFog3rlk=
|
golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps=
|
||||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211031064116-611d5d643895/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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *goBlog) serveFs(fs embed.FS, basePath string) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fileName := strings.TrimPrefix(r.URL.Path, basePath)
|
||||||
|
fb, err := fs.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
a.serve404(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch path.Ext(fileName) {
|
||||||
|
case ".js":
|
||||||
|
w.Header().Set(contentType, contenttype.JS)
|
||||||
|
_, _ = a.min.Write(w, contenttype.JSUTF8, fb)
|
||||||
|
case ".css":
|
||||||
|
w.Header().Set(contentType, contenttype.CSS)
|
||||||
|
_, _ = a.min.Write(w, contenttype.CSSUTF8, fb)
|
||||||
|
default:
|
||||||
|
w.Header().Set(contentType, http.DetectContentType(fb))
|
||||||
|
_, _ = w.Write(fb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -339,6 +339,7 @@ func (a *goBlog) blogEditorRouter(conf *configBlog) func(r chi.Router) {
|
||||||
r.Get("/unlisted", a.serveUnlisted)
|
r.Get("/unlisted", a.serveUnlisted)
|
||||||
r.Get("/unlisted"+feedPath, a.serveUnlisted)
|
r.Get("/unlisted"+feedPath, a.serveUnlisted)
|
||||||
r.Get("/unlisted"+paginationPath, a.serveUnlisted)
|
r.Get("/unlisted"+paginationPath, a.serveUnlisted)
|
||||||
|
r.HandleFunc("/preview", a.serveEditorPreview)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +404,7 @@ func (a *goBlog) blogGeoMapRouter(conf *configBlog) func(r chi.Router) {
|
||||||
r.Use(a.privateModeHandler)
|
r.Use(a.privateModeHandler)
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.With(a.cacheMiddleware).Get("/", a.serveGeoMap)
|
r.With(a.cacheMiddleware).Get("/", a.serveGeoMap)
|
||||||
r.With(cacheLoggedIn, a.cacheMiddleware).HandleFunc("/leaflet/*", a.serveLeaflet(mapPath+"/"))
|
r.With(cacheLoggedIn, a.cacheMiddleware).HandleFunc("/leaflet/*", a.serveFs(leafletFiles, mapPath+"/"))
|
||||||
})
|
})
|
||||||
r.Get("/tiles/{z}/{x}/{y}.png", a.proxyTiles(mapPath+"/tiles"))
|
r.Get("/tiles/{z}/{x}/{y}.png", a.proxyTiles(mapPath+"/tiles"))
|
||||||
})
|
})
|
||||||
|
|
|
@ -237,6 +237,12 @@ details summary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
padding: 10px;
|
||||||
|
@include color-border(border, 1px, solid, primary);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,13 @@ details summary > *:first-child {
|
||||||
background: var(--background, #fff);
|
background: var(--background, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border: 1px solid var(--primary, #000);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
(function () {
|
||||||
|
Array.from(document.querySelectorAll('.mdpreview')).forEach(element => {
|
||||||
|
// Get preview container
|
||||||
|
let previewContainer = document.getElementById(element.dataset.preview)
|
||||||
|
if (!previewContainer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get websocket path
|
||||||
|
let wsUrl = element.dataset.previewws
|
||||||
|
if (!wsUrl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create and open websocket
|
||||||
|
let ws = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + wsUrl)
|
||||||
|
ws.onopen = function () {
|
||||||
|
console.log("Preview-Websocket opened")
|
||||||
|
previewContainer.classList.add('preview')
|
||||||
|
previewContainer.classList.remove('hide')
|
||||||
|
if (ws) {
|
||||||
|
ws.send(element.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ws.onclose = function () {
|
||||||
|
console.log("Preview-Websocket closed")
|
||||||
|
previewContainer.classList.add('hide')
|
||||||
|
previewContainer.classList.remove('preview')
|
||||||
|
ws = null
|
||||||
|
}
|
||||||
|
ws.onmessage = function (evt) {
|
||||||
|
// Set preview HTML
|
||||||
|
previewContainer.innerHTML = evt.data
|
||||||
|
}
|
||||||
|
ws.onerror = function (evt) {
|
||||||
|
console.log("Preview-Websocket error: " + evt.data)
|
||||||
|
}
|
||||||
|
// Add listener
|
||||||
|
element.addEventListener('input', function () {
|
||||||
|
if (ws) {
|
||||||
|
ws.send(element.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})()
|
|
@ -9,22 +9,27 @@
|
||||||
{{ md (editorpostdesc .BlogString) }}
|
{{ md (editorpostdesc .BlogString) }}
|
||||||
<form class="fw p" method="post">
|
<form class="fw p" method="post">
|
||||||
<input type="hidden" name="h" value="entry">
|
<input type="hidden" name="h" value="entry">
|
||||||
<textarea name="content" class="monospace h400p formcache" id="create-input">{{ editortemplate .BlogString }}</textarea>
|
<textarea name="content" class="monospace h400p formcache mdpreview" id="create-input" data-preview="post-preview" data-previewws="{{ .Blog.RelativePath "/editor/preview" }}">{{ editortemplate .BlogString }}</textarea>
|
||||||
|
<div id="post-preview" class="hide"></div>
|
||||||
<input type="submit" value="{{ string .Blog.Lang "create" }}">
|
<input type="submit" value="{{ string .Blog.Lang "create" }}">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{ if .Data.UpdatePostURL }}
|
{{ if .Data.UpdatePostURL }}
|
||||||
<h2 id="update">{{ string .Blog.Lang "update" }}</h2>
|
<h2 id="update">{{ string .Blog.Lang "update" }}</h2>
|
||||||
<form class="fw p" method="post" action="#update">
|
<form class="fw p" method="post" action="#update">
|
||||||
<input type="hidden" name="editoraction" value="updatepost">
|
<input type="hidden" name="editoraction" value="updatepost">
|
||||||
<input type="hidden" name="url" value="{{ .Data.UpdatePostURL }}">
|
<input type="hidden" name="url" value="{{ .Data.UpdatePostURL }}">
|
||||||
<textarea name="content" class="monospace h400p">{{ .Data.UpdatePostContent }}</textarea>
|
<textarea name="content" class="monospace h400p mdpreview" data-preview="update-preview" data-previewws="{{ .Blog.RelativePath "/editor/preview" }}">{{ .Data.UpdatePostContent }}</textarea>
|
||||||
|
<div id="update-preview" class="hide"></div>
|
||||||
<input type="submit" value="{{ string .Blog.Lang "update" }}">
|
<input type="submit" value="{{ string .Blog.Lang "update" }}">
|
||||||
</form>
|
</form>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<h2>{{ string .Blog.Lang "posts" }}</h2>
|
<h2>{{ string .Blog.Lang "posts" }}</h2>
|
||||||
<p><a href="{{ .Blog.RelativePath "/editor/drafts" }}">{{ string .Blog.Lang "drafts" }}</a></p>
|
<p><a href="{{ .Blog.RelativePath "/editor/drafts" }}">{{ string .Blog.Lang "drafts" }}</a></p>
|
||||||
<p><a href="{{ .Blog.RelativePath "/editor/private" }}">{{ string .Blog.Lang "privateposts" }}</a></p>
|
<p><a href="{{ .Blog.RelativePath "/editor/private" }}">{{ string .Blog.Lang "privateposts" }}</a></p>
|
||||||
<p><a href="{{ .Blog.RelativePath "/editor/unlisted" }}">{{ string .Blog.Lang "unlistedposts" }}</a></p>
|
<p><a href="{{ .Blog.RelativePath "/editor/unlisted" }}">{{ string .Blog.Lang "unlistedposts" }}</a></p>
|
||||||
|
|
||||||
<h2>{{ string .Blog.Lang "upload" }}</h2>
|
<h2>{{ string .Blog.Lang "upload" }}</h2>
|
||||||
<form class="fw p" method="post" enctype="multipart/form-data">
|
<form class="fw p" method="post" enctype="multipart/form-data">
|
||||||
<input type="hidden" name="editoraction" value="upload">
|
<input type="hidden" name="editoraction" value="upload">
|
||||||
|
@ -32,11 +37,14 @@
|
||||||
<input type="submit" value="{{ string .Blog.Lang "upload" }}">
|
<input type="submit" value="{{ string .Blog.Lang "upload" }}">
|
||||||
</form>
|
</form>
|
||||||
<p><a href="{{ .Blog.RelativePath "/editor/files" }}">{{ string .Blog.Lang "mediafiles" }}</a></p>
|
<p><a href="{{ .Blog.RelativePath "/editor/files" }}">{{ string .Blog.Lang "mediafiles" }}</a></p>
|
||||||
|
|
||||||
<h2>{{ string .Blog.Lang "location" }}</h2>
|
<h2>{{ string .Blog.Lang "location" }}</h2>
|
||||||
<form class="fw p">
|
<form class="fw p">
|
||||||
<input id="geobtn" type="button" value="{{ string .Blog.Lang "locationget" }}" data-failed="{{ string .Blog.Lang "locationfailed" }}" data-notsupported="{{ string .Blog.Lang "locationnotsupported" }}">
|
<input id="geobtn" type="button" value="{{ string .Blog.Lang "locationget" }}" data-failed="{{ string .Blog.Lang "locationfailed" }}" data-notsupported="{{ string .Blog.Lang "locationnotsupported" }}">
|
||||||
<input id="geostatus" type="text" class="hide" readonly>
|
<input id="geostatus" type="text" class="hide" readonly>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<script defer src="{{ asset "js/mdpreview.js" }}"></script>
|
||||||
<script defer src="{{ asset "js/geohelper.js" }}"></script>
|
<script defer src="{{ asset "js/geohelper.js" }}"></script>
|
||||||
<script defer src="{{ asset "js/formcache.js" }}"></script>
|
<script defer src="{{ asset "js/formcache.js" }}"></script>
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Reference in New Issue