Merge branch 'develop' into chorus
This commit is contained in:
commit
3cc397ad76
6
app.go
6
app.go
|
@ -120,6 +120,8 @@ type Apper interface {
|
||||||
SaveConfig(*config.Config) error
|
SaveConfig(*config.Config) error
|
||||||
|
|
||||||
LoadKeys() error
|
LoadKeys() error
|
||||||
|
|
||||||
|
ReqLog(r *http.Request, status int, timeSince time.Duration) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// App returns the App
|
// App returns the App
|
||||||
|
@ -179,6 +181,10 @@ func (app *App) LoadKeys() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *App) ReqLog(r *http.Request, status int, timeSince time.Duration) string {
|
||||||
|
return fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, timeSince, r.UserAgent())
|
||||||
|
}
|
||||||
|
|
||||||
// handleViewHome shows page at root path. Will be the Pad if logged in and the
|
// handleViewHome shows page at root path. Will be the Pad if logged in and the
|
||||||
// catch-all landing page otherwise.
|
// catch-all landing page otherwise.
|
||||||
func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
|
func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
|
@ -211,6 +211,10 @@ func (c *Collection) DisplayCanonicalURL() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collection) RedirectingCanonicalURL(isRedir bool) string {
|
func (c *Collection) RedirectingCanonicalURL(isRedir bool) string {
|
||||||
|
if c.hostName == "" {
|
||||||
|
// If this is true, the human programmers screwed up. So ask for a bug report and fail, fail, fail
|
||||||
|
log.Error("[PROGRAMMER ERROR] WARNING: Collection.hostName is empty! Federation and many other things will fail! If you're seeing this in the wild, please report this bug and let us know what you were doing just before this: https://github.com/writeas/writefreely/issues/new?template=bug_report.md")
|
||||||
|
}
|
||||||
if isSingleUser {
|
if isSingleUser {
|
||||||
return c.hostName + "/"
|
return c.hostName + "/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ type writestore interface {
|
||||||
GetAnonymousPosts(u *User) (*[]PublicPost, error)
|
GetAnonymousPosts(u *User) (*[]PublicPost, error)
|
||||||
GetUserPosts(u *User) (*[]PublicPost, error)
|
GetUserPosts(u *User) (*[]PublicPost, error)
|
||||||
|
|
||||||
CreateOwnedPost(post *SubmittedPost, accessToken, collAlias string) (*PublicPost, error)
|
CreateOwnedPost(post *SubmittedPost, accessToken, collAlias, hostName string) (*PublicPost, error)
|
||||||
CreatePost(userID, collID int64, post *SubmittedPost) (*Post, error)
|
CreatePost(userID, collID int64, post *SubmittedPost) (*Post, error)
|
||||||
UpdateOwnedPost(post *AuthenticatedPost, userID int64) error
|
UpdateOwnedPost(post *AuthenticatedPost, userID int64) error
|
||||||
GetEditablePost(id, editToken string) (*PublicPost, error)
|
GetEditablePost(id, editToken string) (*PublicPost, error)
|
||||||
|
@ -542,7 +542,7 @@ func (db *datastore) GetTemporaryOneTimeAccessToken(userID int64, validSecs int,
|
||||||
return u.String(), nil
|
return u.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias string) (*PublicPost, error) {
|
func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias, hostName string) (*PublicPost, error) {
|
||||||
var userID, collID int64 = -1, -1
|
var userID, collID int64 = -1, -1
|
||||||
var coll *Collection
|
var coll *Collection
|
||||||
var err error
|
var err error
|
||||||
|
@ -556,6 +556,7 @@ func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
coll.hostName = hostName
|
||||||
if coll.OwnerID != userID {
|
if coll.OwnerID != userID {
|
||||||
return nil, ErrForbiddenCollection
|
return nil, ErrForbiddenCollection
|
||||||
}
|
}
|
||||||
|
|
28
handle.go
28
handle.go
|
@ -137,7 +137,7 @@ func (h *Handler) User(f userHandlerFunc) http.HandlerFunc {
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
u := getUserSession(h.app.App(), r)
|
u := getUserSession(h.app.App(), r)
|
||||||
|
@ -175,7 +175,7 @@ func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc {
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()))
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
u := getUserSession(h.app.App(), r)
|
u := getUserSession(h.app.App(), r)
|
||||||
|
@ -213,7 +213,7 @@ func (h *Handler) AdminApper(f userApperHandlerFunc) http.HandlerFunc {
|
||||||
status = http.StatusInternalServerError
|
status = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()))
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
u := getUserSession(h.app.App(), r)
|
u := getUserSession(h.app.App(), r)
|
||||||
|
@ -295,7 +295,7 @@ func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerF
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
u, err := a(h.app.App(), r)
|
u, err := a(h.app.App(), r)
|
||||||
|
@ -381,7 +381,7 @@ func (h *Handler) WebErrors(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var session *sessions.Session
|
var session *sessions.Session
|
||||||
|
@ -440,7 +440,7 @@ func (h *Handler) CollectionPostOrStatic(w http.ResponseWriter, r *http.Request)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
status := 200
|
status := 200
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Serve static file
|
// Serve static file
|
||||||
|
@ -472,7 +472,7 @@ func (h *Handler) Web(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if ul(h.app.App().cfg) != UserLevelNoneType {
|
if ul(h.app.App().cfg) != UserLevelNoneType {
|
||||||
|
@ -530,7 +530,7 @@ func (h *Handler) All(f handlerFunc) http.HandlerFunc {
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// TODO: do any needed authentication
|
// TODO: do any needed authentication
|
||||||
|
@ -562,7 +562,7 @@ func (h *Handler) AllReader(f handlerFunc) http.HandlerFunc {
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if h.app.App().cfg.App.Private {
|
if h.app.App().cfg.App.Private {
|
||||||
|
@ -619,7 +619,7 @@ func (h *Handler) Download(f dataHandlerFunc, ul UserLevelFunc) http.HandlerFunc
|
||||||
status = 500
|
status = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
data, filename, err := f(h.app.App(), w, r)
|
data, filename, err := f(h.app.App(), w, r)
|
||||||
|
@ -682,7 +682,7 @@ func (h *Handler) Redirect(url string, ul UserLevelFunc) http.HandlerFunc {
|
||||||
|
|
||||||
status = sendRedirect(w, http.StatusFound, url)
|
status = sendRedirect(w, http.StatusFound, url)
|
||||||
|
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}())
|
}())
|
||||||
|
@ -721,6 +721,10 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er
|
||||||
return
|
return
|
||||||
} else if err.Status == http.StatusNotFound {
|
} else if err.Status == http.StatusNotFound {
|
||||||
w.WriteHeader(err.Status)
|
w.WriteHeader(err.Status)
|
||||||
|
if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
|
||||||
|
// This is a fediverse request; simply return the header
|
||||||
|
return
|
||||||
|
}
|
||||||
h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
|
h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
|
||||||
return
|
return
|
||||||
} else if err.Status == http.StatusInternalServerError {
|
} else if err.Status == http.StatusInternalServerError {
|
||||||
|
@ -799,7 +803,7 @@ func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: log actual status code returned
|
// TODO: log actual status code returned
|
||||||
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
|
log.Info(h.app.ReqLog(r, status, time.Since(start)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if h.app.App().cfg.App.Private {
|
if h.app.App().cfg.App.Private {
|
||||||
|
|
44
posts.go
44
posts.go
|
@ -557,7 +557,7 @@ func newPost(app *App, w http.ResponseWriter, r *http.Request) error {
|
||||||
var coll *Collection
|
var coll *Collection
|
||||||
var err error
|
var err error
|
||||||
if accessToken != "" {
|
if accessToken != "" {
|
||||||
newPost, err = app.db.CreateOwnedPost(p, accessToken, collAlias)
|
newPost, err = app.db.CreateOwnedPost(p, accessToken, collAlias, app.cfg.App.Host)
|
||||||
} else {
|
} else {
|
||||||
//return ErrNotLoggedIn
|
//return ErrNotLoggedIn
|
||||||
// TODO: verify user is logged in
|
// TODO: verify user is logged in
|
||||||
|
@ -1300,14 +1300,32 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
|
||||||
coll.Owner = owner
|
coll.Owner = owner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postFound := true
|
||||||
p, err := app.db.GetPost(slug, coll.ID)
|
p, err := app.db.GetPost(slug, coll.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ErrCollectionPageNotFound && slug == "feed" {
|
if err == ErrCollectionPageNotFound {
|
||||||
// User tried to access blog feed without a trailing slash, and
|
postFound = false
|
||||||
// there's no post with a slug "feed"
|
|
||||||
return impart.HTTPError{http.StatusFound, c.CanonicalURL() + "/feed/"}
|
if slug == "feed" {
|
||||||
|
// User tried to access blog feed without a trailing slash, and
|
||||||
|
// there's no post with a slug "feed"
|
||||||
|
return impart.HTTPError{http.StatusFound, c.CanonicalURL() + "/feed/"}
|
||||||
|
}
|
||||||
|
|
||||||
|
po := &Post{
|
||||||
|
Slug: null.NewString(slug, true),
|
||||||
|
Font: "norm",
|
||||||
|
Language: zero.NewString("en", true),
|
||||||
|
RTL: zero.NewBool(false, true),
|
||||||
|
Content: `<p class="msg">This page is missing.</p>
|
||||||
|
|
||||||
|
Are you sure it was ever here?`,
|
||||||
|
}
|
||||||
|
pp := po.processPost()
|
||||||
|
p = &pp
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
p.IsOwner = owner != nil && p.OwnerID.Valid && owner.ID == p.OwnerID.Int64
|
p.IsOwner = owner != nil && p.OwnerID.Valid && owner.ID == p.OwnerID.Int64
|
||||||
p.Collection = coll
|
p.Collection = coll
|
||||||
|
@ -1329,11 +1347,20 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
|
||||||
contentType = "text/markdown"
|
contentType = "text/markdown"
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", fmt.Sprintf("%s; charset=utf-8", contentType))
|
w.Header().Set("Content-Type", fmt.Sprintf("%s; charset=utf-8", contentType))
|
||||||
|
if !postFound {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, "Post not found.")
|
||||||
|
// TODO: return error instead, so status is correctly reflected in logs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if isMarkdown && p.Title.String != "" {
|
if isMarkdown && p.Title.String != "" {
|
||||||
fmt.Fprintf(w, "# %s\n\n", p.Title.String)
|
fmt.Fprintf(w, "# %s\n\n", p.Title.String)
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, p.Content)
|
fmt.Fprint(w, p.Content)
|
||||||
} else if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
|
} else if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
|
||||||
|
if !postFound {
|
||||||
|
return ErrCollectionPageNotFound
|
||||||
|
}
|
||||||
p.extractData()
|
p.extractData()
|
||||||
ap := p.ActivityObject(app.cfg)
|
ap := p.ActivityObject(app.cfg)
|
||||||
ap.Context = []interface{}{activitystreams.Namespace}
|
ap.Context = []interface{}{activitystreams.Namespace}
|
||||||
|
@ -1350,6 +1377,7 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
|
||||||
IsPinned bool
|
IsPinned bool
|
||||||
IsCustomDomain bool
|
IsCustomDomain bool
|
||||||
PinnedPosts *[]PublicPost
|
PinnedPosts *[]PublicPost
|
||||||
|
IsFound bool
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
CanInvite bool
|
CanInvite bool
|
||||||
}{
|
}{
|
||||||
|
@ -1357,12 +1385,16 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
|
||||||
StaticPage: pageForReq(app, r),
|
StaticPage: pageForReq(app, r),
|
||||||
IsOwner: cr.isCollOwner,
|
IsOwner: cr.isCollOwner,
|
||||||
IsCustomDomain: cr.isCustomDomain,
|
IsCustomDomain: cr.isCustomDomain,
|
||||||
|
IsFound: postFound,
|
||||||
}
|
}
|
||||||
tp.IsAdmin = u != nil && u.IsAdmin()
|
tp.IsAdmin = u != nil && u.IsAdmin()
|
||||||
tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin)
|
tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin)
|
||||||
tp.PinnedPosts, _ = app.db.GetPinnedPosts(coll)
|
tp.PinnedPosts, _ = app.db.GetPinnedPosts(coll)
|
||||||
tp.IsPinned = len(*tp.PinnedPosts) > 0 && PostsContains(tp.PinnedPosts, p)
|
tp.IsPinned = len(*tp.PinnedPosts) > 0 && PostsContains(tp.PinnedPosts, p)
|
||||||
|
|
||||||
|
if !postFound {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
postTmpl := "collection-post"
|
postTmpl := "collection-post"
|
||||||
if app.cfg.App.Chorus {
|
if app.cfg.App.Chorus {
|
||||||
postTmpl = "chorus-collection-post"
|
postTmpl = "chorus-collection-post"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="/css/write.css" />
|
<link rel="stylesheet" type="text/css" href="/css/write.css" />
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
{{ if .IsFound }}
|
||||||
<link rel="canonical" href="{{.CanonicalURL}}" />
|
<link rel="canonical" href="{{.CanonicalURL}}" />
|
||||||
<meta name="generator" content="WriteFreely">
|
<meta name="generator" content="WriteFreely">
|
||||||
<meta name="title" content="{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
|
<meta name="title" content="{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
<meta property="og:updated_time" content="{{.Created8601}}" />
|
<meta property="og:updated_time" content="{{.Created8601}}" />
|
||||||
{{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="{{.Collection.AvatarURL}}">{{end}}
|
{{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="{{.Collection.AvatarURL}}">{{end}}
|
||||||
<meta property="article:published_time" content="{{.Created8601}}">
|
<meta property="article:published_time" content="{{.Created8601}}">
|
||||||
|
{{ end }}
|
||||||
{{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
|
{{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
|
||||||
|
|
||||||
{{if .Collection.RenderMathJax}}
|
{{if .Collection.RenderMathJax}}
|
||||||
|
@ -50,14 +52,14 @@
|
||||||
{{if .PinnedPosts}}
|
{{if .PinnedPosts}}
|
||||||
{{range .PinnedPosts}}<a class="pinned{{if eq .Slug.String $.Slug.String}} selected{{end}}" href="{{if not $.SingleUser}}/{{$.Collection.Alias}}/{{.Slug.String}}{{else}}{{.CanonicalURL}}{{end}}">{{.PlainDisplayTitle}}</a>{{end}}
|
{{range .PinnedPosts}}<a class="pinned{{if eq .Slug.String $.Slug.String}} selected{{end}}" href="{{if not $.SingleUser}}/{{$.Collection.Alias}}/{{.Slug.String}}{{else}}{{.CanonicalURL}}{{end}}">{{.PlainDisplayTitle}}</a>{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ if .IsOwner }}<span class="views" dir="ltr"><strong>{{largeNumFmt .Views}}</strong> {{pluralize "view" "views" .Views}}</span>
|
{{ if and .IsOwner .IsFound }}<span class="views" dir="ltr"><strong>{{largeNumFmt .Views}}</strong> {{pluralize "view" "views" .Views}}</span>
|
||||||
<a class="xtra-feature" href="/{{if not .SingleUser}}{{.Collection.Alias}}/{{end}}{{.Slug.String}}/edit" dir="{{.Direction}}">Edit</a>
|
<a class="xtra-feature" href="/{{if not .SingleUser}}{{.Collection.Alias}}/{{end}}{{.Slug.String}}/edit" dir="{{.Direction}}">Edit</a>
|
||||||
{{if .IsPinned}}<a class="xtra-feature unpin" href="/{{.Collection.Alias}}/{{.Slug.String}}/unpin" dir="{{.Direction}}" onclick="unpinPost(event, '{{.ID}}')">Unpin</a>{{end}}
|
{{if .IsPinned}}<a class="xtra-feature unpin" href="/{{.Collection.Alias}}/{{.Slug.String}}/unpin" dir="{{.Direction}}" onclick="unpinPost(event, '{{.ID}}')">Unpin</a>{{end}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article id="post-body" class="{{.Font}} h-entry">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
|
<article id="post-body" class="{{.Font}} h-entry {{if not .IsFound}}error-page{{end}}">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
|
||||||
|
|
||||||
{{ if .Collection.ShowFooterBranding }}
|
{{ if .Collection.ShowFooterBranding }}
|
||||||
<footer dir="ltr"><hr><nav><p style="font-size: 0.9em">{{localhtml "published with write.as" .Language.String}}</p></nav></footer>
|
<footer dir="ltr"><hr><nav><p style="font-size: 0.9em">{{localhtml "published with write.as" .Language.String}}</p></nav></footer>
|
||||||
|
|
Loading…
Reference in New Issue