diff --git a/admin.go b/admin.go index 6d3cffd..c5e762b 100644 --- a/admin.go +++ b/admin.go @@ -3,6 +3,7 @@ package writefreely import ( "fmt" "github.com/gogits/gogs/pkg/tool" + "github.com/gorilla/mux" "github.com/writeas/impart" "github.com/writeas/web-core/auth" "net/http" @@ -62,16 +63,47 @@ func handleViewAdminDash(app *app, u *User, w http.ResponseWriter, r *http.Reque *UserPage Message string SysStatus systemStatus + + AboutPage, PrivacyPage string }{ - NewUserPage(app, r, u, "Admin", nil), - r.FormValue("m"), - sysStatus, + UserPage: NewUserPage(app, r, u, "Admin", nil), + Message: r.FormValue("m"), + SysStatus: sysStatus, + } + + var err error + p.AboutPage, err = getAboutPage(app) + if err != nil { + return err + } + + p.PrivacyPage, _, err = getPrivacyPage(app) + if err != nil { + return err } showUserPage(w, "admin", p) return nil } +func handleAdminUpdateSite(app *app, u *User, w http.ResponseWriter, r *http.Request) error { + vars := mux.Vars(r) + id := vars["page"] + + // Validate + if id != "about" && id != "privacy" { + return impart.HTTPError{http.StatusNotFound, "No such page."} + } + + // Update page + m := "" + err := app.db.UpdateDynamicContent(id, r.FormValue("content")) + if err != nil { + m = "?m=" + err.Error() + } + return impart.HTTPError{http.StatusFound, "/admin" + m + "#page-" + id} +} + func updateAppStats() { sysStatus.Uptime = tool.TimeSincePro(appStartTime) diff --git a/app.go b/app.go index 91f5cb1..0514f38 100644 --- a/app.go +++ b/app.go @@ -93,6 +93,42 @@ func handleViewHome(app *app, w http.ResponseWriter, r *http.Request) error { return renderPage(w, "landing.tmpl", p) } +func handleTemplatedPage(app *app, w http.ResponseWriter, r *http.Request, t *template.Template) error { + p := struct { + page.StaticPage + Content template.HTML + Updated string + }{ + StaticPage: pageForReq(app, r), + } + if r.URL.Path == "/about" || r.URL.Path == "/privacy" { + var c string + var updated *time.Time + var err error + + if r.URL.Path == "/about" { + c, err = getAboutPage(app) + } else { + c, updated, err = getPrivacyPage(app) + } + + if err != nil { + return err + } + p.Content = template.HTML(applyMarkdown([]byte(c))) + if updated != nil { + p.Updated = updated.Format("January 2, 2006") + } + } + + // Serve templated page + err := t.ExecuteTemplate(w, "base", p) + if err != nil { + log.Error("Unable to render page: %v", err) + } + return nil +} + func pageForReq(app *app, r *http.Request) page.StaticPage { p := page.StaticPage{ AppCfg: app.cfg.App, diff --git a/database.go b/database.go index b9888e6..0c79e10 100644 --- a/database.go +++ b/database.go @@ -91,6 +91,9 @@ type writestore interface { GetAPFollowers(c *Collection) (*[]RemoteUser, error) GetAPActorKeys(collectionID int64) ([]byte, []byte) + + GetDynamicContent(id string) (string, *time.Time, error) + UpdateDynamicContent(id, content string) error } type datastore struct { @@ -2105,6 +2108,28 @@ func (db *datastore) GetAPActorKeys(collectionID int64) ([]byte, []byte) { return pub, priv } +func (db *datastore) GetDynamicContent(id string) (string, *time.Time, error) { + var c string + var u *time.Time + err := db.QueryRow("SELECT content, updated FROM appcontent WHERE id = ?", id).Scan(&c, &u) + switch { + case err == sql.ErrNoRows: + return "", nil, nil + case err != nil: + log.Error("Couldn't SELECT FROM appcontent for id '%s': %v", id, err) + return "", nil, err + } + return c, u, nil +} + +func (db *datastore) UpdateDynamicContent(id, content string) error { + _, err := db.Exec("INSERT INTO appcontent (id, content, updated) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE content = ?, updated = NOW()", id, content, content) + if err != nil { + log.Error("Unable to INSERT appcontent for '%s': %v", id, err) + } + return err +} + func stringLogln(log *string, s string, v ...interface{}) { *log += fmt.Sprintf(s+"\n", v...) } diff --git a/less/admin.less b/less/admin.less new file mode 100644 index 0000000..4f6e8ac --- /dev/null +++ b/less/admin.less @@ -0,0 +1,4 @@ +.edit-page { + font-size: 1em; + min-height: 12em; +} diff --git a/less/app.less b/less/app.less index 7f32c1c..ec3472d 100644 --- a/less/app.less +++ b/less/app.less @@ -4,6 +4,7 @@ @import "pad-theme"; @import "post-temp"; @import "effects"; +@import "admin"; @import "pages/error"; @import "lib/elements"; @import "lib/material"; diff --git a/pages.go b/pages.go new file mode 100644 index 0000000..901faec --- /dev/null +++ b/pages.go @@ -0,0 +1,39 @@ +package writefreely + +import ( + "time" +) + +func getAboutPage(app *app) (string, error) { + c, _, err := app.db.GetDynamicContent("about") + if err != nil { + return "", err + } + if c == "" { + if app.cfg.App.Federation { + c = `_` + app.cfg.App.SiteName + `_ is an interconnected place for you to write and publish, powered by WriteFreely and ActivityPub.` + } else { + c = `_` + app.cfg.App.SiteName + `_ is a place for you to write and publish, powered by WriteFreely.` + } + } + return c, nil +} + +func getPrivacyPage(app *app) (string, *time.Time, error) { + c, updated, err := app.db.GetDynamicContent("privacy") + if err != nil { + return "", nil, err + } + if c == "" { + c = `[Write Freely](https://writefreely.org), the software that powers this site, is built to enforce your right to privacy by default. + +It retains as little data about you as possible, not even requiring an email address to sign up. However, if you _do_ give us your email address, it is stored encrypted in our database. We salt and hash your account's password. + +We store log files, or data about what happens on our servers. We also use cookies to keep you logged in to your account. + +Beyond this, it's important that you trust whoever runs **` + app.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.` + defaultTime := time.Date(2018, 11, 8, 12, 0, 0, 0, time.Local) + updated = &defaultTime + } + return c, updated, nil +} diff --git a/pages/about.tmpl b/pages/about.tmpl index c816e5b..a47faf6 100644 --- a/pages/about.tmpl +++ b/pages/about.tmpl @@ -4,18 +4,7 @@
- {{ if .Federation }} - {{.SiteName}} is an interconnected place for you to write and publish, powered by WriteFreely and ActivityPub. - {{ else }} - {{.SiteName}} is a place for you to write and publish, powered by WriteFreely. - {{ end }} -
- + {{.Content}}WriteFreely is a self-hosted, decentralized blogging platform for publishing beautiful, simple blogs.
diff --git a/pages/privacy.tmpl b/pages/privacy.tmpl index 7d778b2..a32d028 100644 --- a/pages/privacy.tmpl +++ b/pages/privacy.tmpl @@ -2,10 +2,8 @@ {{end}} {{define "content"}}Last updated November 8, 2018
-Write Freely, the software that powers this site, is built to enforce your right to privacy by default.
-It retains as little data about you as possible, not even requiring an email address to sign up. However, if you do give us your email address, it is stored encrypted in our database. We salt and hash your account's password.
-We store log files, or data about what happens on our servers. We also use cookies to keep you logged in to your account.
-Beyond this, it's important that you trust whoever runs {{.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.
+Last updated {{.Updated}}
+ + {{.Content}}{{.Message}}
{{end}}Describe what your instance is about. Accepts Markdown.
+ + +Outline your privacy policy. Accepts Markdown.
+ + +writefreely --reset-pass <username>
+
+