diff --git a/account.go b/account.go index ba013c2..fb1550d 100644 --- a/account.go +++ b/account.go @@ -826,6 +826,9 @@ func viewEditCollection(app *App, u *User, w http.ResponseWriter, r *http.Reques return ErrCollectionNotFound } + // Add collection properties + c.MonetizationPointer = app.db.GetCollectionAttribute(c.ID, "monetization_pointer") + silenced, err := app.db.IsUserSilenced(u.ID) if err != nil { log.Error("view edit collection %v", err) diff --git a/admin.go b/admin.go index 457b384..a0d10eb 100644 --- a/admin.go +++ b/admin.go @@ -529,6 +529,7 @@ func handleAdminUpdateConfig(apper Apper, u *User, w http.ResponseWriter, r *htt } apper.App().cfg.App.Federation = r.FormValue("federation") == "on" apper.App().cfg.App.PublicStats = r.FormValue("public_stats") == "on" + apper.App().cfg.App.Monetization = r.FormValue("monetization") == "on" apper.App().cfg.App.Private = r.FormValue("private") == "on" apper.App().cfg.App.LocalTimeline = r.FormValue("local_timeline") == "on" if apper.App().cfg.App.LocalTimeline && apper.App().timeline == nil { diff --git a/collections.go b/collections.go index 2295837..e1ebe48 100644 --- a/collections.go +++ b/collections.go @@ -56,6 +56,8 @@ type ( PublicOwner bool `datastore:"public_owner" json:"-"` URL string `json:"url,omitempty"` + MonetizationPointer string `json:"monetization_pointer,omitempty"` + db *datastore hostName string } @@ -87,14 +89,15 @@ type ( Handle string `schema:"handle" json:"handle"` // Actual collection values updated in the DB - Alias *string `schema:"alias" json:"alias"` - Title *string `schema:"title" json:"title"` - Description *string `schema:"description" json:"description"` - StyleSheet *sql.NullString `schema:"style_sheet" json:"style_sheet"` - Script *sql.NullString `schema:"script" json:"script"` - Signature *sql.NullString `schema:"signature" json:"signature"` - Visibility *int `schema:"visibility" json:"public"` - Format *sql.NullString `schema:"format" json:"format"` + Alias *string `schema:"alias" json:"alias"` + Title *string `schema:"title" json:"title"` + Description *string `schema:"description" json:"description"` + StyleSheet *sql.NullString `schema:"style_sheet" json:"style_sheet"` + Script *sql.NullString `schema:"script" json:"script"` + Signature *sql.NullString `schema:"signature" json:"signature"` + Monetization *string `schema:"monetization_pointer" json:"monetization_pointer"` + Visibility *int `schema:"visibility" json:"public"` + Format *sql.NullString `schema:"format" json:"format"` } CollectionFormat struct { Format string @@ -552,6 +555,7 @@ type CollectionPage struct { IsOwner bool CanPin bool Username string + Monetization string Collections *[]Collection PinnedPosts *[]PublicPost IsAdmin bool @@ -829,6 +833,7 @@ func handleViewCollection(app *App, w http.ResponseWriter, r *http.Request) erro // Add more data // TODO: fix this mess of collections inside collections displayPage.PinnedPosts, _ = app.db.GetPinnedPosts(coll.CollectionObj, isOwner) + displayPage.Monetization = app.db.GetCollectionAttribute(coll.ID, "monetization_pointer") collTmpl := "collection" if app.cfg.App.Chorus { @@ -947,6 +952,7 @@ func handleViewCollectionTag(app *App, w http.ResponseWriter, r *http.Request) e // Add more data // TODO: fix this mess of collections inside collections displayPage.PinnedPosts, _ = app.db.GetPinnedPosts(coll.CollectionObj, isOwner) + displayPage.Monetization = app.db.GetCollectionAttribute(coll.ID, "monetization_pointer") err = templates["collection-tags"].ExecuteTemplate(w, "collection-tags", displayPage) if err != nil { diff --git a/config/config.go b/config/config.go index 7b64e02..faf73fb 100644 --- a/config/config.go +++ b/config/config.go @@ -1,5 +1,5 @@ /* - * Copyright © 2018-2019 A Bunch Tell LLC. + * Copyright © 2018-2020 A Bunch Tell LLC. * * This file is part of WriteFreely. * @@ -137,9 +137,11 @@ type ( MinUsernameLen int `ini:"min_username_len"` MaxBlogs int `ini:"max_blogs"` + // Options for public instances // Federation - Federation bool `ini:"federation"` - PublicStats bool `ini:"public_stats"` + Federation bool `ini:"federation"` + PublicStats bool `ini:"public_stats"` + Monetization bool `ini:"monetization"` // Access Private bool `ini:"private"` diff --git a/database.go b/database.go index 8237e41..54939fe 100644 --- a/database.go +++ b/database.go @@ -905,6 +905,29 @@ func (db *datastore) UpdateCollection(c *SubmittedCollection, alias string) erro } } + // Update Monetization value + if c.Monetization != nil { + skipUpdate := false + if *c.Monetization != "" { + // Strip away any excess spaces + trimmed := strings.TrimSpace(*c.Monetization) + // Only update value when it starts with "$", per spec: https://paymentpointers.org + if strings.HasPrefix(trimmed, "$") { + c.Monetization = &trimmed + } else { + // Value appears invalid, so don't update + skipUpdate = true + } + } + if !skipUpdate { + _, err = db.Exec("INSERT INTO collectionattributes (collection_id, attribute, value) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE value = ?", collID, "monetization_pointer", *c.Monetization, *c.Monetization) + if err != nil { + log.Error("Unable to insert monetization_pointer value: %v", err) + return err + } + } + } + // Update rest of the collection data res, err = db.Exec("UPDATE collections SET "+q.Updates+" WHERE "+q.Conditions, q.Params...) if err != nil { @@ -2162,6 +2185,28 @@ func (db *datastore) CollectionHasAttribute(id int64, attr string) bool { return true } +func (db *datastore) GetCollectionAttribute(id int64, attr string) string { + var v string + err := db.QueryRow("SELECT value FROM collectionattributes WHERE collection_id = ? AND attribute = ?", id, attr).Scan(&v) + switch { + case err == sql.ErrNoRows: + return "" + case err != nil: + log.Error("Couldn't SELECT value in getCollectionAttribute for attribute '%s': %v", attr, err) + return "" + } + return v +} + +func (db *datastore) SetCollectionAttribute(id int64, attr, v string) error { + _, err := db.Exec("INSERT INTO collectionattributes (collection_id, attribute, value) VALUES (?, ?, ?)", id, attr, v) + if err != nil { + log.Error("Unable to INSERT into collectionattributes: %v", err) + return err + } + return nil +} + // DeleteAccount will delete the entire account for userID func (db *datastore) DeleteAccount(userID int64) error { // Get all collections diff --git a/posts.go b/posts.go index 4c8c76e..8d60650 100644 --- a/posts.go +++ b/posts.go @@ -1476,6 +1476,7 @@ Are you sure it was ever here?`, IsOwner bool IsPinned bool IsCustomDomain bool + Monetization string PinnedPosts *[]PublicPost IsFound bool IsAdmin bool @@ -1493,6 +1494,7 @@ Are you sure it was ever here?`, tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin) tp.PinnedPosts, _ = app.db.GetPinnedPosts(coll, p.IsOwner) tp.IsPinned = len(*tp.PinnedPosts) > 0 && PostsContains(tp.PinnedPosts, p) + tp.Monetization = app.db.GetCollectionAttribute(coll.ID, "monetization_pointer") if !postFound { w.WriteHeader(http.StatusNotFound) diff --git a/templates/chorus-collection-post.tmpl b/templates/chorus-collection-post.tmpl index dcea457..22f2d8f 100644 --- a/templates/chorus-collection-post.tmpl +++ b/templates/chorus-collection-post.tmpl @@ -29,6 +29,7 @@ {{range .Images}}{{else}}{{end}} + {{template "collection-meta" .}} {{if .Collection.StyleSheet}}{{end}} {{end}} {{end}} {{if .Collection.RenderMathJax}} diff --git a/templates/collection-tags.tmpl b/templates/collection-tags.tmpl index b7c92c8..e2f8962 100644 --- a/templates/collection-tags.tmpl +++ b/templates/collection-tags.tmpl @@ -29,6 +29,7 @@ + {{template "collection-meta" .}} {{if .Collection.StyleSheet}}{{end}} {{if .Collection.RenderMathJax}} diff --git a/templates/collection.tmpl b/templates/collection.tmpl index d39c58c..42664e7 100644 --- a/templates/collection.tmpl +++ b/templates/collection.tmpl @@ -27,6 +27,7 @@ + {{template "collection-meta" .}} {{if .StyleSheet}}{{end}} {{if .RenderMathJax}} diff --git a/templates/include/post-render.tmpl b/templates/include/post-render.tmpl index 81fd33e..c4ed082 100644 --- a/templates/include/post-render.tmpl +++ b/templates/include/post-render.tmpl @@ -1,4 +1,10 @@ +{{define "collection-meta"}} + {{if .Monetization -}} + + {{- end}} +{{end}} + {{define "highlighting"}}