|
|
@ -2,9 +2,12 @@ package writefreely |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"bytes" |
|
|
|
"bytes" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"github.com/microcosm-cc/bluemonday" |
|
|
|
"github.com/microcosm-cc/bluemonday" |
|
|
|
stripmd "github.com/writeas/go-strip-markdown" |
|
|
|
stripmd "github.com/writeas/go-strip-markdown" |
|
|
|
"github.com/writeas/saturday" |
|
|
|
"github.com/writeas/saturday" |
|
|
|
|
|
|
|
"github.com/writeas/web-core/stringmanip" |
|
|
|
|
|
|
|
"github.com/writeas/writefreely/parse" |
|
|
|
"html" |
|
|
|
"html" |
|
|
|
"html/template" |
|
|
|
"html/template" |
|
|
|
"regexp" |
|
|
|
"regexp" |
|
|
@ -122,6 +125,30 @@ func postTitle(content, friendlyId string) string { |
|
|
|
return friendlyId |
|
|
|
return friendlyId |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: fix duplicated code from postTitle. postTitle is a widely used func we
|
|
|
|
|
|
|
|
// don't have time to investigate right now.
|
|
|
|
|
|
|
|
func friendlyPostTitle(content, friendlyId string) string { |
|
|
|
|
|
|
|
const maxTitleLen = 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Strip HTML tags with bluemonday's StrictPolicy, then unescape the HTML
|
|
|
|
|
|
|
|
// entities added in by sanitizing the content.
|
|
|
|
|
|
|
|
content = html.UnescapeString(bluemonday.StrictPolicy().Sanitize(content)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
content = strings.TrimLeftFunc(stripmd.Strip(content), unicode.IsSpace) |
|
|
|
|
|
|
|
eol := strings.IndexRune(content, '\n') |
|
|
|
|
|
|
|
blankLine := strings.Index(content, "\n\n") |
|
|
|
|
|
|
|
if blankLine != -1 && blankLine <= eol && blankLine <= assumedTitleLen { |
|
|
|
|
|
|
|
return strings.TrimSpace(content[:blankLine]) |
|
|
|
|
|
|
|
} else if eol == -1 && utf8.RuneCountInString(content) <= maxTitleLen { |
|
|
|
|
|
|
|
return content |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
title, truncd := parse.TruncToWord(parse.PostLede(content, true), maxTitleLen) |
|
|
|
|
|
|
|
if truncd { |
|
|
|
|
|
|
|
title += "..." |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return title |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func getSanitizationPolicy() *bluemonday.Policy { |
|
|
|
func getSanitizationPolicy() *bluemonday.Policy { |
|
|
|
policy := bluemonday.UGCPolicy() |
|
|
|
policy := bluemonday.UGCPolicy() |
|
|
|
policy.AllowAttrs("src", "style").OnElements("iframe", "video") |
|
|
|
policy.AllowAttrs("src", "style").OnElements("iframe", "video") |
|
|
@ -133,3 +160,62 @@ func getSanitizationPolicy() *bluemonday.Policy { |
|
|
|
policy.AllowURLSchemes("http", "https", "mailto", "xmpp") |
|
|
|
policy.AllowURLSchemes("http", "https", "mailto", "xmpp") |
|
|
|
return policy |
|
|
|
return policy |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func sanitizePost(content string) string { |
|
|
|
|
|
|
|
return strings.Replace(content, "<", "<", -1) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// postDescription generates a description based on the given post content,
|
|
|
|
|
|
|
|
// title, and post ID. This doesn't consider a V2 post field, `title` when
|
|
|
|
|
|
|
|
// choosing what to generate. In case a post has a title, this function will
|
|
|
|
|
|
|
|
// fail, and logic should instead be implemented to skip this when there's no
|
|
|
|
|
|
|
|
// title, like so:
|
|
|
|
|
|
|
|
// var desc string
|
|
|
|
|
|
|
|
// if title == "" {
|
|
|
|
|
|
|
|
// desc = postDescription(content, title, friendlyId)
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// desc = shortPostDescription(content)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
func postDescription(content, title, friendlyId string) string { |
|
|
|
|
|
|
|
maxLen := 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if content == "" { |
|
|
|
|
|
|
|
content = "Write Freely is a painless, simple, federated blogging platform." |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
fmtStr := "%s" |
|
|
|
|
|
|
|
truncation := 0 |
|
|
|
|
|
|
|
if utf8.RuneCountInString(content) > maxLen { |
|
|
|
|
|
|
|
// Post is longer than the max description, so let's show a better description
|
|
|
|
|
|
|
|
fmtStr = "%s..." |
|
|
|
|
|
|
|
truncation = 3 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if title == friendlyId { |
|
|
|
|
|
|
|
// No specific title was found; simply truncate the post, starting at the beginning
|
|
|
|
|
|
|
|
content = fmt.Sprintf(fmtStr, strings.Replace(stringmanip.Substring(content, 0, maxLen-truncation), "\n", " ", -1)) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// There was a title, so return a real description
|
|
|
|
|
|
|
|
blankLine := strings.Index(content, "\n\n") |
|
|
|
|
|
|
|
if blankLine < 0 { |
|
|
|
|
|
|
|
blankLine = 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
truncd := stringmanip.Substring(content, blankLine, blankLine+maxLen-truncation) |
|
|
|
|
|
|
|
contentNoNL := strings.Replace(truncd, "\n", " ", -1) |
|
|
|
|
|
|
|
content = strings.TrimSpace(fmt.Sprintf(fmtStr, contentNoNL)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return content |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func shortPostDescription(content string) string { |
|
|
|
|
|
|
|
maxLen := 140 |
|
|
|
|
|
|
|
fmtStr := "%s" |
|
|
|
|
|
|
|
truncation := 0 |
|
|
|
|
|
|
|
if utf8.RuneCountInString(content) > maxLen { |
|
|
|
|
|
|
|
// Post is longer than the max description, so let's show a better description
|
|
|
|
|
|
|
|
fmtStr = "%s..." |
|
|
|
|
|
|
|
truncation = 3 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return strings.TrimSpace(fmt.Sprintf(fmtStr, strings.Replace(stringmanip.Substring(content, 0, maxLen-truncation), "\n", " ", -1))) |
|
|
|
|
|
|
|
} |
|
|
|