This makes it possible to edit the title and introductory text at the top of the Reader view. Ref T684customize-reader
@@ -319,6 +319,8 @@ func handleViewAdminPage(app *App, u *User, w http.ResponseWriter, r *http.Reque | |||||
} | } | ||||
p.Content, err = getLandingBody(app) | p.Content, err = getLandingBody(app) | ||||
p.Content.ID = "landing" | p.Content.ID = "landing" | ||||
} else if slug == "reader" { | |||||
p.Content, err = getReaderSection(app) | |||||
} else { | } else { | ||||
p.Content, err = app.db.GetDynamicContent(slug) | p.Content, err = app.db.GetDynamicContent(slug) | ||||
} | } | ||||
@@ -342,7 +344,7 @@ func handleAdminUpdateSite(app *App, u *User, w http.ResponseWriter, r *http.Req | |||||
id := vars["page"] | id := vars["page"] | ||||
// Validate | // Validate | ||||
if id != "about" && id != "privacy" && id != "landing" { | |||||
if id != "about" && id != "privacy" && id != "landing" && id != "reader" { | |||||
return impart.HTTPError{http.StatusNotFound, "No such page."} | return impart.HTTPError{http.StatusNotFound, "No such page."} | ||||
} | } | ||||
@@ -356,6 +358,9 @@ func handleAdminUpdateSite(app *App, u *User, w http.ResponseWriter, r *http.Req | |||||
return impart.HTTPError{http.StatusFound, "/admin/page/" + id + m} | return impart.HTTPError{http.StatusFound, "/admin/page/" + id + m} | ||||
} | } | ||||
err = app.db.UpdateDynamicContent("landing-body", "", r.FormValue("content"), "section") | err = app.db.UpdateDynamicContent("landing-body", "", r.FormValue("content"), "section") | ||||
} else if id == "reader" { | |||||
// Update sections with titles | |||||
err = app.db.UpdateDynamicContent(id, r.FormValue("title"), r.FormValue("content"), "section") | |||||
} else { | } else { | ||||
// Update page | // Update page | ||||
err = app.db.UpdateDynamicContent(id, r.FormValue("title"), r.FormValue("content"), "page") | err = app.db.UpdateDynamicContent(id, r.FormValue("title"), r.FormValue("content"), "page") | ||||
@@ -135,3 +135,30 @@ WriteFreely can communicate with other federated platforms like Mastodon, so peo | |||||
} | } | ||||
return "" | return "" | ||||
} | } | ||||
func getReaderSection(app *App) (*instanceContent, error) { | |||||
c, err := app.db.GetDynamicContent("reader") | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if c == nil { | |||||
c = &instanceContent{ | |||||
ID: "reader", | |||||
Type: "section", | |||||
Content: defaultReaderBanner(app.cfg), | |||||
Updated: defaultPageUpdatedTime, | |||||
} | |||||
} | |||||
if !c.Title.Valid { | |||||
c.Title = defaultReaderTitle(app.cfg) | |||||
} | |||||
return c, nil | |||||
} | |||||
func defaultReaderTitle(cfg *config.Config) sql.NullString { | |||||
return sql.NullString{String: "Reader", Valid: true} | |||||
} | |||||
func defaultReaderBanner(cfg *config.Config) string { | |||||
return "Read the latest posts from " + cfg.App.SiteName + "." | |||||
} |
@@ -50,6 +50,10 @@ type readPublication struct { | |||||
SelTopic string | SelTopic string | ||||
IsAdmin bool | IsAdmin bool | ||||
CanInvite bool | CanInvite bool | ||||
// Customizable page content | |||||
ContentTitle string | |||||
Content template.HTML | |||||
} | } | ||||
func initLocalTimeline(app *App) { | func initLocalTimeline(app *App) { | ||||
@@ -211,8 +215,14 @@ func showLocalTimeline(app *App, w http.ResponseWriter, r *http.Request, page in | |||||
d.IsAdmin = u != nil && u.IsAdmin() | d.IsAdmin = u != nil && u.IsAdmin() | ||||
d.CanInvite = canUserInvite(app.cfg, d.IsAdmin) | d.CanInvite = canUserInvite(app.cfg, d.IsAdmin) | ||||
} | } | ||||
c, err := getReaderSection(app) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
d.ContentTitle = c.Title.String | |||||
d.Content = template.HTML(applyMarkdown([]byte(c.Content), "", app.cfg)) | |||||
err := templates["read"].ExecuteTemplate(w, "base", d) | |||||
err = templates["read"].ExecuteTemplate(w, "base", d) | |||||
if err != nil { | if err != nil { | ||||
log.Error("Unable to render reader: %v", err) | log.Error("Unable to render reader: %v", err) | ||||
fmt.Fprintf(w, ":(") | fmt.Fprintf(w, ":(") | ||||
@@ -80,8 +80,8 @@ | |||||
{{define "body-attrs"}}id="collection"{{end}} | {{define "body-attrs"}}id="collection"{{end}} | ||||
{{define "content"}} | {{define "content"}} | ||||
<div class="content-container snug" style="max-width: 40rem;"> | <div class="content-container snug" style="max-width: 40rem;"> | ||||
<h1 style="text-align:center">Reader</h1> | |||||
<p{{if .SelTopic}} style="text-align:center"{{end}}>{{if .SelTopic}}#{{.SelTopic}} posts{{else}}Read the latest posts from {{.SiteName}}. {{if .Username}}To showcase your writing here, go to your <a href="/me/c/">blog</a> settings and select the <em>Public</em> option.{{end}}{{end}}</p> | |||||
<h1>{{.ContentTitle}}</h1> | |||||
<p{{if .SelTopic}} style="text-align:center"{{end}}>{{if .SelTopic}}#{{.SelTopic}} posts{{else}}{{.Content}}{{end}}</p> | |||||
</div> | </div> | ||||
<div id="wrapper"> | <div id="wrapper"> | ||||
{{ if gt (len .Posts) 0 }} | {{ if gt (len .Posts) 0 }} | ||||
@@ -20,6 +20,9 @@ table.classy.export .disabled, table.classy.export a { | |||||
<tr> | <tr> | ||||
<td colspan="2"><a href="/admin/page/landing">Home</a></td> | <td colspan="2"><a href="/admin/page/landing">Home</a></td> | ||||
</tr> | </tr> | ||||
{{if .LocalTimeline}}<tr> | |||||
<td colspan="2"><a href="/admin/page/reader">Reader</a></td> | |||||
</tr>{{end}} | |||||
{{range .Pages}} | {{range .Pages}} | ||||
<tr> | <tr> | ||||
<td><a href="/admin/page/{{.ID}}">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.ID}}{{end}}</a></td> | <td><a href="/admin/page/{{.ID}}">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.ID}}{{end}}</a></td> | ||||
@@ -31,6 +31,8 @@ input[type=text] { | |||||
<p class="page-desc content-desc">Describe what your instance is <a href="/about" target="page">about</a>.</p> | <p class="page-desc content-desc">Describe what your instance is <a href="/about" target="page">about</a>.</p> | ||||
{{else if eq .Content.ID "privacy"}} | {{else if eq .Content.ID "privacy"}} | ||||
<p class="page-desc content-desc">Outline your <a href="/privacy" target="page">privacy policy</a>.</p> | <p class="page-desc content-desc">Outline your <a href="/privacy" target="page">privacy policy</a>.</p> | ||||
{{else if eq .Content.ID "reader"}} | |||||
<p class="page-desc content-desc">Customize your <a href="/read" target="page">Reader</a> page.</p> | |||||
{{else if eq .Content.ID "landing"}} | {{else if eq .Content.ID "landing"}} | ||||
<p class="page-desc content-desc">Customize your <a href="/?landing=1" target="page">home page</a>.</p> | <p class="page-desc content-desc">Customize your <a href="/?landing=1" target="page">home page</a>.</p> | ||||
{{end}} | {{end}} | ||||
@@ -38,7 +40,7 @@ input[type=text] { | |||||
{{if .Message}}<p>{{.Message}}</p>{{end}} | {{if .Message}}<p>{{.Message}}</p>{{end}} | ||||
<form method="post" action="/admin/update/{{.Content.ID}}" onsubmit="savePage(this)"> | <form method="post" action="/admin/update/{{.Content.ID}}" onsubmit="savePage(this)"> | ||||
{{if eq .Content.Type "section"}} | |||||
{{if .Banner}} | |||||
<label for="banner"> | <label for="banner"> | ||||
Banner | Banner | ||||
</label> | </label> | ||||