Make landing page dynamic

This enables admins to customize their landing / home page via the Admin
dashboard -- including the text at the top of the page and the section
below it. It keeps the current default text, falling back to it if the
user hasn't overwritten it.

Ref T565
This commit is contained in:
Matt Baer 2019-06-27 17:06:37 -04:00
parent aedb05080c
commit a72ce2ef29
6 changed files with 122 additions and 21 deletions

View File

@ -299,6 +299,7 @@ func handleViewAdminPage(app *App, u *User, w http.ResponseWriter, r *http.Reque
Config config.AppCfg
Message string
Banner *instanceContent
Content *instanceContent
}{
Config: app.cfg.App,
@ -311,6 +312,13 @@ func handleViewAdminPage(app *App, u *User, w http.ResponseWriter, r *http.Reque
p.Content, err = getAboutPage(app)
} else if slug == "privacy" {
p.Content, err = getPrivacyPage(app)
} else if slug == "landing" {
p.Banner, err = getLandingBanner(app)
if err != nil {
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get banner: %v", err)}
}
p.Content, err = getLandingPage(app)
p.Content.ID = "landing"
} else {
p.Content, err = app.db.GetDynamicContent(slug)
}
@ -334,13 +342,24 @@ func handleAdminUpdateSite(app *App, u *User, w http.ResponseWriter, r *http.Req
id := vars["page"]
// Validate
if id != "about" && id != "privacy" {
if id != "about" && id != "privacy" && id != "landing" {
return impart.HTTPError{http.StatusNotFound, "No such page."}
}
// Update page
var err error
m := ""
err := app.db.UpdateDynamicContent(id, r.FormValue("title"), r.FormValue("content"), "page")
if id == "landing" {
// Handle special landing page
err = app.db.UpdateDynamicContent("landing-banner", "", r.FormValue("banner"), "section")
if err != nil {
m = "?m=" + err.Error()
return impart.HTTPError{http.StatusFound, "/admin/page/" + id + m}
}
err = app.db.UpdateDynamicContent("landing-body", "", r.FormValue("content"), "section")
} else {
// Update page
err = app.db.UpdateDynamicContent(id, r.FormValue("title"), r.FormValue("content"), "page")
}
if err != nil {
m = "?m=" + err.Error()
}

16
app.go
View File

@ -203,6 +203,8 @@ func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
p := struct {
page.StaticPage
Flashes []template.HTML
Banner template.HTML
Content template.HTML
ForcedLanding bool
}{
@ -210,6 +212,20 @@ func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
ForcedLanding: forceLanding,
}
banner, err := getLandingBanner(app)
if err != nil {
log.Error("unable to get landing banner: %v", err)
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get banner: %v", err)}
}
p.Banner = template.HTML(applyMarkdown([]byte(banner.Content), ""))
content, err := getLandingPage(app)
if err != nil {
log.Error("unable to get landing content: %v", err)
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get content: %v", err)}
}
p.Content = template.HTML(applyMarkdown([]byte(content.Content), ""))
// Get error messages
session, err := app.sessionStore.Get(r, cookieName)
if err != nil {

View File

@ -79,3 +79,59 @@ We store log files, or data about what happens on our servers. We also use cooki
Beyond this, it's important that you trust whoever runs **` + cfg.App.SiteName + `**. Software can only do so much to protect you -- your level of privacy protections will ultimately fall on the humans that run this particular service.`
}
func getLandingBanner(app *App) (*instanceContent, error) {
c, err := app.db.GetDynamicContent("landing-banner")
if err != nil {
return nil, err
}
if c == nil {
c = &instanceContent{
ID: "landing-banner",
Type: "section",
Content: defaultLandingBanner(app.cfg),
Updated: defaultPageUpdatedTime,
}
}
return c, nil
}
func getLandingPage(app *App) (*instanceContent, error) {
c, err := app.db.GetDynamicContent("landing-body")
if err != nil {
return nil, err
}
if c == nil {
c = &instanceContent{
ID: "landing-body",
Type: "section",
Content: defaultLandingBody(app.cfg),
Updated: defaultPageUpdatedTime,
}
}
return c, nil
}
func defaultLandingBanner(cfg *config.Config) string {
if cfg.App.Federation {
return "# Start your blog in the fediverse"
}
return "# Start your blog"
}
func defaultLandingBody(cfg *config.Config) string {
if cfg.App.Federation {
return `## Join the Fediverse
The fediverse is a large network of platforms that all speak a common language. Imagine if you could reply to Instagram posts from Twitter, or interact with your favorite Medium blogs from Facebook -- federated alternatives like [PixelFed](https://pixelfed.org), [Mastodon](https://joinmastodon.org), and WriteFreely enable you to do these types of things.
<div style="text-align:center">
<iframe style="width: 560px; height: 315px; max-width: 100%;" sandbox="allow-same-origin allow-scripts" src="https://video.writeas.org/videos/embed/cc55e615-d204-417c-9575-7b57674cc6f3" frameborder="0" allowfullscreen></iframe>
</div>
## Write More Socially
WriteFreely can communicate with other federated platforms like Mastodon, so people can follow your blogs, bookmark their favorite posts, and boost them to their followers. Sign up above to create a blog and join the fediverse.`
}
return ""
}

View File

@ -53,15 +53,22 @@ tr.subscription {
form dd {
margin: 0;
}
.banner-container {
text-align: left;
}
.banner-container h1 {
margin-top: 0;
max-width: 8em;
}
</style>
{{end}}
{{define "content"}}
<div id="pricing" class="content-container wide-form">
<div class="row">
<div style="text-align:left">
<h1 style="margin-top:0;max-width:8em;">{{if .Federation}}Start your blog in the fediverse{{else}}Start your blog{{end}}</h1>
<p><a href="{{if .Federation}}#more{{else}}/about{{end}}">Learn more...</a></p>
<div class="banner-container">
{{.Banner}}
<p><a href="{{if .Content}}#more{{else}}/about{{end}}">Learn more...</a></p>
</div>
<div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}>
@ -101,24 +108,14 @@ form dd {
</div>
</div>
{{if .Federation}}
{{if .Content}}
<a name="more"></a><hr style="margin: 1em auto 3em;" />
{{end}}
</div>
{{ if .Federation }}
{{ if .Content }}
<div class="content-container snug">
<h2>Join the Fediverse</h2>
<p>The fediverse is a large network of platforms that all speak a common language. Imagine if you could reply to Instagram posts from Twitter, or interact with your favorite Medium blogs from Facebook &mdash; federated alternatives like <a href="https://pixelfed.org/" target="pixel">PixelFed</a>, <a href="https://joinmastodon.org/" target="masto">Mastodon</a>, and WriteFreely enable you to do these types of things.</p>
<div style="text-align:center">
<iframe style="width: 560px; height: 315px; max-width: 100%;" sandbox="allow-same-origin allow-scripts" src="https://video.writeas.org/videos/embed/cc55e615-d204-417c-9575-7b57674cc6f3" frameborder="0" allowfullscreen></iframe>
</div>
<h2>Write More Socially</h2>
<p>WriteFreely can communicate with other federated platforms like Mastodon, so people can follow your blogs, bookmark their favorite posts, and boost them to their followers. Sign up above to create a blog and join the fediverse.</p>
{{.Content}}
</div>
{{ end }}

View File

@ -17,6 +17,9 @@ table.classy.export .disabled, table.classy.export a {
<th>Page</th>
<th>Last Modified</th>
</tr>
<tr>
<td colspan="2"><a href="/admin/page/landing">Home</a></td>
</tr>
{{range .Pages}}
<tr>
<td><a href="/admin/page/{{.ID}}">{{if .Title.Valid}}{{.Title.String}}{{else}}{{.ID}}{{end}}</a></td>

View File

@ -25,23 +25,33 @@ input[type=text] {
<div class="snug content-container">
{{template "admin-header" .}}
<h2 id="posts-header">{{.Content.ID}} page</h2>
<h2 id="posts-header">{{if eq .Content.ID "landing"}}Home page{{else}}{{.Content.ID}} page{{end}}</h2>
{{if eq .Content.ID "about"}}
<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"}}
<p class="page-desc content-desc">Outline your <a href="/privacy" target="page">privacy policy</a>.</p>
{{else if eq .Content.ID "landing"}}
<p class="page-desc content-desc">Customize your <a href="/?landing=1">home page</a>.</p>
{{end}}
{{if .Message}}<p>{{.Message}}</p>{{end}}
<form method="post" action="/admin/update/{{.Content.ID}}" onsubmit="savePage(this)">
{{if eq .Content.Type "section"}}
<label for="banner">
Banner
</label>
<textarea id="banner" class="section codable norm edit-page" style="min-height: 5em; height: 5em;" name="banner">{{.Banner.Content}}</textarea>
<p class="content-desc">We suggest a header (e.g. <code># Welcome</code>), optionally followed by a small bit of text. Accepts Markdown and HTML.</p>
{{else}}
<label for="title">
Title
</label>
<input type="text" name="title" id="title" value="{{.Content.Title.String}}" />
{{end}}
<label for="content">
Content
{{if .Banner}}Body{{else}}Content{{end}}
</label>
<textarea id="content" class="section codable norm edit-page" name="content">{{.Content.Content}}</textarea>