Support post signatures
This enables users to add a signature to all blog posts, and update it from a single location. Requires database migration with: writefreely db migrate Closes T582
This commit is contained in:
parent
5c94d23466
commit
a25664bb97
|
@ -47,6 +47,7 @@ type (
|
||||||
Language string `schema:"lang" json:"lang,omitempty"`
|
Language string `schema:"lang" json:"lang,omitempty"`
|
||||||
StyleSheet string `datastore:"style_sheet" schema:"style_sheet" json:"style_sheet"`
|
StyleSheet string `datastore:"style_sheet" schema:"style_sheet" json:"style_sheet"`
|
||||||
Script string `datastore:"script" schema:"script" json:"script,omitempty"`
|
Script string `datastore:"script" schema:"script" json:"script,omitempty"`
|
||||||
|
Signature string `datastore:"post_signature" schema:"signature" json:"-"`
|
||||||
Public bool `datastore:"public" json:"public"`
|
Public bool `datastore:"public" json:"public"`
|
||||||
Visibility collVisibility `datastore:"private" json:"-"`
|
Visibility collVisibility `datastore:"private" json:"-"`
|
||||||
Format string `datastore:"format" json:"format,omitempty"`
|
Format string `datastore:"format" json:"format,omitempty"`
|
||||||
|
@ -91,6 +92,7 @@ type (
|
||||||
Description *string `schema:"description" json:"description"`
|
Description *string `schema:"description" json:"description"`
|
||||||
StyleSheet *sql.NullString `schema:"style_sheet" json:"style_sheet"`
|
StyleSheet *sql.NullString `schema:"style_sheet" json:"style_sheet"`
|
||||||
Script *sql.NullString `schema:"script" json:"script"`
|
Script *sql.NullString `schema:"script" json:"script"`
|
||||||
|
Signature *sql.NullString `schema:"signature" json:"signature"`
|
||||||
Visibility *int `schema:"visibility" json:"public"`
|
Visibility *int `schema:"visibility" json:"public"`
|
||||||
Format *sql.NullString `schema:"format" json:"format"`
|
Format *sql.NullString `schema:"format" json:"format"`
|
||||||
}
|
}
|
||||||
|
|
13
database.go
13
database.go
|
@ -791,10 +791,10 @@ func (db *datastore) GetCollectionBy(condition string, value interface{}) (*Coll
|
||||||
c := &Collection{}
|
c := &Collection{}
|
||||||
|
|
||||||
// FIXME: change Collection to reflect database values. Add helper functions to get actual values
|
// FIXME: change Collection to reflect database values. Add helper functions to get actual values
|
||||||
var styleSheet, script, format zero.String
|
var styleSheet, script, signature, format zero.String
|
||||||
row := db.QueryRow("SELECT id, alias, title, description, style_sheet, script, format, owner_id, privacy, view_count FROM collections WHERE "+condition, value)
|
row := db.QueryRow("SELECT id, alias, title, description, style_sheet, script, post_signature, format, owner_id, privacy, view_count FROM collections WHERE "+condition, value)
|
||||||
|
|
||||||
err := row.Scan(&c.ID, &c.Alias, &c.Title, &c.Description, &styleSheet, &script, &format, &c.OwnerID, &c.Visibility, &c.Views)
|
err := row.Scan(&c.ID, &c.Alias, &c.Title, &c.Description, &styleSheet, &script, &signature, &format, &c.OwnerID, &c.Visibility, &c.Views)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
return nil, impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
return nil, impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
||||||
|
@ -806,6 +806,7 @@ func (db *datastore) GetCollectionBy(condition string, value interface{}) (*Coll
|
||||||
}
|
}
|
||||||
c.StyleSheet = styleSheet.String
|
c.StyleSheet = styleSheet.String
|
||||||
c.Script = script.String
|
c.Script = script.String
|
||||||
|
c.Signature = signature.String
|
||||||
c.Format = format.String
|
c.Format = format.String
|
||||||
c.Public = c.IsPublic()
|
c.Public = c.IsPublic()
|
||||||
|
|
||||||
|
@ -849,7 +850,8 @@ func (db *datastore) UpdateCollection(c *SubmittedCollection, alias string) erro
|
||||||
SetStringPtr(c.Title, "title").
|
SetStringPtr(c.Title, "title").
|
||||||
SetStringPtr(c.Description, "description").
|
SetStringPtr(c.Description, "description").
|
||||||
SetNullString(c.StyleSheet, "style_sheet").
|
SetNullString(c.StyleSheet, "style_sheet").
|
||||||
SetNullString(c.Script, "script")
|
SetNullString(c.Script, "script").
|
||||||
|
SetNullString(c.Signature, "post_signature")
|
||||||
|
|
||||||
if c.Format != nil {
|
if c.Format != nil {
|
||||||
cf := &CollectionFormat{Format: c.Format.String}
|
cf := &CollectionFormat{Format: c.Format.String}
|
||||||
|
@ -1150,6 +1152,7 @@ func (db *datastore) GetPosts(cfg *config.Config, c *Collection, page int, inclu
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.extractData()
|
p.extractData()
|
||||||
|
p.augmentContent(c)
|
||||||
p.formatContent(cfg, c, includeFuture)
|
p.formatContent(cfg, c, includeFuture)
|
||||||
|
|
||||||
posts = append(posts, p.processPost())
|
posts = append(posts, p.processPost())
|
||||||
|
@ -1214,6 +1217,7 @@ func (db *datastore) GetPostsTagged(cfg *config.Config, c *Collection, tag strin
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.extractData()
|
p.extractData()
|
||||||
|
p.augmentContent(c)
|
||||||
p.formatContent(cfg, c, includeFuture)
|
p.formatContent(cfg, c, includeFuture)
|
||||||
|
|
||||||
posts = append(posts, p.processPost())
|
posts = append(posts, p.processPost())
|
||||||
|
@ -1590,6 +1594,7 @@ func (db *datastore) GetPinnedPosts(coll *CollectionObj, includeFuture bool) (*[
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
p.extractData()
|
p.extractData()
|
||||||
|
p.augmentContent(&coll.Collection)
|
||||||
|
|
||||||
pp := p.processPost()
|
pp := p.processPost()
|
||||||
pp.Collection = coll
|
pp.Collection = coll
|
||||||
|
|
|
@ -78,3 +78,10 @@ func (db *datastore) engine() string {
|
||||||
}
|
}
|
||||||
return " ENGINE = InnoDB"
|
return " ENGINE = InnoDB"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) after(colName string) string {
|
||||||
|
if db.driverName == driverSQLite {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return " AFTER " + colName
|
||||||
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ var migrations = []Migration{
|
||||||
New("support oauth attach", oauthAttach), // V6 -> V7
|
New("support oauth attach", oauthAttach), // V6 -> V7
|
||||||
New("support oauth via invite", oauthInvites), // V7 -> V8 (v0.12.0)
|
New("support oauth via invite", oauthInvites), // V7 -> V8 (v0.12.0)
|
||||||
New("optimize drafts retrieval", optimizeDrafts), // V8 -> V9
|
New("optimize drafts retrieval", optimizeDrafts), // V8 -> V9
|
||||||
|
New("support post signatures", supportPostSignatures), // V9 -> V10
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentVer returns the current migration version the application is on
|
// CurrentVer returns the current migration version the application is on
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2020 A Bunch Tell LLC.
|
||||||
|
*
|
||||||
|
* This file is part of WriteFreely.
|
||||||
|
*
|
||||||
|
* WriteFreely is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License, included
|
||||||
|
* in the LICENSE file in this source code package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
func supportPostSignatures(db *datastore) error {
|
||||||
|
t, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = t.Exec(`ALTER TABLE collections ADD COLUMN post_signature ` + db.typeText() + db.collateMultiByte() + ` NULL` + db.after("script"))
|
||||||
|
if err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Commit()
|
||||||
|
if err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -58,6 +58,17 @@ func (p *PublicPost) formatContent(cfg *config.Config, isOwner bool) {
|
||||||
p.Post.formatContent(cfg, &p.Collection.Collection, isOwner)
|
p.Post.formatContent(cfg, &p.Collection.Collection, isOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Post) augmentContent(c *Collection) {
|
||||||
|
// Add post signatures
|
||||||
|
if c.Signature != "" {
|
||||||
|
p.Content += "\n\n" + c.Signature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PublicPost) augmentContent() {
|
||||||
|
p.Post.augmentContent(&p.Collection.Collection)
|
||||||
|
}
|
||||||
|
|
||||||
func applyMarkdown(data []byte, baseURL string, cfg *config.Config) string {
|
func applyMarkdown(data []byte, baseURL string, cfg *config.Config) string {
|
||||||
return applyMarkdownSpecial(data, false, baseURL, cfg)
|
return applyMarkdownSpecial(data, false, baseURL, cfg)
|
||||||
}
|
}
|
||||||
|
|
3
posts.go
3
posts.go
|
@ -1140,6 +1140,7 @@ func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object {
|
||||||
p.Collection.FederatedAccount() + "/followers",
|
p.Collection.FederatedAccount() + "/followers",
|
||||||
}
|
}
|
||||||
o.Name = p.DisplayTitle()
|
o.Name = p.DisplayTitle()
|
||||||
|
p.augmentContent()
|
||||||
if p.HTMLContent == template.HTML("") {
|
if p.HTMLContent == template.HTML("") {
|
||||||
p.formatContent(cfg, false)
|
p.formatContent(cfg, false)
|
||||||
}
|
}
|
||||||
|
@ -1430,6 +1431,8 @@ Are you sure it was ever here?`,
|
||||||
return impart.HTTPError{http.StatusGone, "Post was unpublished."}
|
return impart.HTTPError{http.StatusGone, "Post was unpublished."}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.augmentContent()
|
||||||
|
|
||||||
// Serve collection post
|
// Serve collection post
|
||||||
if isRaw {
|
if isRaw {
|
||||||
contentType := "text/plain"
|
contentType := "text/plain"
|
||||||
|
|
|
@ -5,6 +5,15 @@
|
||||||
{{define "collection"}}
|
{{define "collection"}}
|
||||||
{{template "header" .}}
|
{{template "header" .}}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
textarea.section.norm {
|
||||||
|
font-family: Lora,'Palatino Linotype','Book Antiqua','New York','DejaVu serif',serif !important;
|
||||||
|
min-height: 10em;
|
||||||
|
max-height: 20em;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="content-container snug">
|
<div class="content-container snug">
|
||||||
<div id="overlay"></div>
|
<div id="overlay"></div>
|
||||||
|
|
||||||
|
@ -129,6 +138,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="option">
|
||||||
|
<h2>Post Signature</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p class="explain">This content will be added to the end of every post on this blog, as if it were part of the post itself. Markdown, HTML, and shortcodes are allowed.</p>
|
||||||
|
<textarea id="signature" class="section norm" name="signature">{{.Signature}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="option" style="text-align: center; margin-top: 4em;">
|
<div class="option" style="text-align: center; margin-top: 4em;">
|
||||||
<input type="submit" id="save-changes" value="Save changes" />
|
<input type="submit" id="save-changes" value="Save changes" />
|
||||||
<p><a href="{{if .SingleUser}}/{{else}}/{{.Alias}}/{{end}}">View Blog</a></p>
|
<p><a href="{{if .SingleUser}}/{{else}}/{{.Alias}}/{{end}}">View Blog</a></p>
|
||||||
|
|
Loading…
Reference in New Issue