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
pull/131/head
Matt Baer 5 years ago
parent aedb05080c
commit a72ce2ef29
  1. 25
      admin.go
  2. 16
      app.go
  3. 56
      pages.go
  4. 29
      pages/landing.tmpl
  5. 3
      templates/user/admin/pages.tmpl
  6. 14
      templates/user/admin/view-page.tmpl

@ -299,6 +299,7 @@ func handleViewAdminPage(app *App, u *User, w http.ResponseWriter, r *http.Reque
Config config.AppCfg Config config.AppCfg
Message string Message string
Banner *instanceContent
Content *instanceContent Content *instanceContent
}{ }{
Config: app.cfg.App, 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) p.Content, err = getAboutPage(app)
} else if slug == "privacy" { } else if slug == "privacy" {
p.Content, err = getPrivacyPage(app) 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 { } else {
p.Content, err = app.db.GetDynamicContent(slug) 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"] id := vars["page"]
// Validate // Validate
if id != "about" && id != "privacy" { if id != "about" && id != "privacy" && id != "landing" {
return impart.HTTPError{http.StatusNotFound, "No such page."} return impart.HTTPError{http.StatusNotFound, "No such page."}
} }
// Update page var err error
m := "" 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 { if err != nil {
m = "?m=" + err.Error() m = "?m=" + err.Error()
} }

@ -203,6 +203,8 @@ func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
p := struct { p := struct {
page.StaticPage page.StaticPage
Flashes []template.HTML Flashes []template.HTML
Banner template.HTML
Content template.HTML
ForcedLanding bool ForcedLanding bool
}{ }{
@ -210,6 +212,20 @@ func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {
ForcedLanding: forceLanding, 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 // Get error messages
session, err := app.sessionStore.Get(r, cookieName) session, err := app.sessionStore.Get(r, cookieName)
if err != nil { if err != nil {

@ -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.` 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 ""
}

@ -53,15 +53,22 @@ tr.subscription {
form dd { form dd {
margin: 0; margin: 0;
} }
.banner-container {
text-align: left;
}
.banner-container h1 {
margin-top: 0;
max-width: 8em;
}
</style> </style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div id="pricing" class="content-container wide-form"> <div id="pricing" class="content-container wide-form">
<div class="row"> <div class="row">
<div style="text-align:left"> <div class="banner-container">
<h1 style="margin-top:0;max-width:8em;">{{if .Federation}}Start your blog in the fediverse{{else}}Start your blog{{end}}</h1> {{.Banner}}
<p><a href="{{if .Federation}}#more{{else}}/about{{end}}">Learn more...</a></p> <p><a href="{{if .Content}}#more{{else}}/about{{end}}">Learn more...</a></p>
</div> </div>
<div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}> <div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}>
@ -101,24 +108,14 @@ form dd {
</div> </div>
</div> </div>
{{if .Federation}} {{if .Content}}
<a name="more"></a><hr style="margin: 1em auto 3em;" /> <a name="more"></a><hr style="margin: 1em auto 3em;" />
{{end}} {{end}}
</div> </div>
{{ if .Federation }} {{ if .Content }}
<div class="content-container snug"> <div class="content-container snug">
{{.Content}}
<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>
</div> </div>
{{ end }} {{ end }}

@ -17,6 +17,9 @@ table.classy.export .disabled, table.classy.export a {
<th>Page</th> <th>Page</th>
<th>Last Modified</th> <th>Last Modified</th>
</tr> </tr>
<tr>
<td colspan="2"><a href="/admin/page/landing">Home</a></td>
</tr>
{{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>

@ -25,23 +25,33 @@ input[type=text] {
<div class="snug content-container"> <div class="snug content-container">
{{template "admin-header" .}} {{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"}} {{if eq .Content.ID "about"}}
<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 "landing"}}
<p class="page-desc content-desc">Customize your <a href="/?landing=1">home page</a>.</p>
{{end}} {{end}}
{{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"}}
<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"> <label for="title">
Title Title
</label> </label>
<input type="text" name="title" id="title" value="{{.Content.Title.String}}" /> <input type="text" name="title" id="title" value="{{.Content.Title.String}}" />
{{end}}
<label for="content"> <label for="content">
Content {{if .Banner}}Body{{else}}Content{{end}}
</label> </label>
<textarea id="content" class="section codable norm edit-page" name="content">{{.Content.Content}}</textarea> <textarea id="content" class="section codable norm edit-page" name="content">{{.Content.Content}}</textarea>

Loading…
Cancel
Save